summaryrefslogtreecommitdiffstats
path: root/share/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'share/extensions')
-rw-r--r--share/extensions/.cvsignore2
-rw-r--r--share/extensions/Makefile.am99
-rw-r--r--share/extensions/README12
-rw-r--r--share/extensions/SpSVG.pm351
-rw-r--r--share/extensions/addnodes.inx13
-rw-r--r--share/extensions/addnodes.py92
-rw-r--r--share/extensions/ai_input.inx16
-rw-r--r--share/extensions/ai_output.inx16
-rwxr-xr-xshare/extensions/bezmisc.py250
-rw-r--r--share/extensions/bluredge.inx13
-rwxr-xr-xshare/extensions/cubicsuperpath.py83
-rw-r--r--share/extensions/dia.inx15
-rwxr-xr-xshare/extensions/dia2svg.sh13
-rw-r--r--share/extensions/dots.inx14
-rwxr-xr-xshare/extensions/dots.py75
-rw-r--r--share/extensions/dropshadow.inx13
-rw-r--r--share/extensions/dxf_input.inx16
-rw-r--r--share/extensions/dxf_output.inx17
-rwxr-xr-xshare/extensions/embed_raster_in_svg.pl122
-rw-r--r--share/extensions/embedimage.inx12
-rw-r--r--share/extensions/embedimage.py60
-rw-r--r--share/extensions/eps_input.inx17
-rw-r--r--share/extensions/epsi_output.inx17
-rw-r--r--share/extensions/extractimage.inx13
-rw-r--r--share/extensions/extractimage.py50
-rwxr-xr-xshare/extensions/ffgeom.py127
-rw-r--r--share/extensions/ffmet.inx23
-rw-r--r--share/extensions/ffms.inx23
-rwxr-xr-xshare/extensions/ffproc.py194
-rwxr-xr-xshare/extensions/ffscale.py73
-rw-r--r--share/extensions/ffset.inx21
-rw-r--r--share/extensions/ffss.inx21
-rwxr-xr-xshare/extensions/fretfind.py186
-rw-r--r--share/extensions/gimpgrad.inx14
-rw-r--r--share/extensions/grid.inx16
-rw-r--r--share/extensions/handles.inx12
-rwxr-xr-xshare/extensions/handles.py56
-rw-r--r--share/extensions/ill2svg.pl374
-rwxr-xr-xshare/extensions/inkex.py103
-rwxr-xr-xshare/extensions/inkscape-shadow-white.sh2
-rwxr-xr-xshare/extensions/inkscape-shadow.sh2
-rw-r--r--share/extensions/interp.inx17
-rwxr-xr-xshare/extensions/interp.py332
-rw-r--r--share/extensions/kochify.inx12
-rwxr-xr-xshare/extensions/kochify.py86
-rw-r--r--share/extensions/kochify_load.inx12
-rwxr-xr-xshare/extensions/kochify_load.py60
-rw-r--r--share/extensions/lindenmayer.inx17
-rwxr-xr-xshare/extensions/lindenmayer.py88
-rw-r--r--share/extensions/motion.inx14
-rwxr-xr-xshare/extensions/motion.py119
-rw-r--r--share/extensions/pdf_output.inx17
-rw-r--r--share/extensions/pdf_output_via_gs_on_win32.inx24
-rw-r--r--share/extensions/ps2dxf.sh10
-rwxr-xr-xshare/extensions/ps2epsi.sh9
-rw-r--r--share/extensions/ps2pdf.cmd10
-rwxr-xr-xshare/extensions/ps2pdf.sh2
-rw-r--r--share/extensions/ps_input.inx17
-rwxr-xr-xshare/extensions/pturtle.py80
-rw-r--r--share/extensions/radiusrand.inx15
-rwxr-xr-xshare/extensions/radiusrand.py65
-rw-r--r--share/extensions/randompnt.inx11
-rw-r--r--share/extensions/randompos.inx11
-rw-r--r--share/extensions/rtree.inx14
-rwxr-xr-xshare/extensions/rtree.py59
-rwxr-xr-xshare/extensions/simplepath.py201
-rwxr-xr-xshare/extensions/simplestyle.py27
-rwxr-xr-xshare/extensions/sk2svg.sh12
-rw-r--r--share/extensions/sk_input.inx15
-rw-r--r--share/extensions/straightseg.inx14
-rwxr-xr-xshare/extensions/straightseg.py69
-rw-r--r--share/extensions/summersnight.inx12
-rwxr-xr-xshare/extensions/summersnight.py99
-rw-r--r--share/extensions/svg_and_media_zip_output.inx18
-rw-r--r--share/extensions/svg_and_media_zip_output.py123
-rw-r--r--share/extensions/svg_dropshadow88
-rw-r--r--share/extensions/svgz_input.inx17
-rw-r--r--share/extensions/svgz_output.inx17
-rw-r--r--share/extensions/txt2svg.inx16
-rwxr-xr-xshare/extensions/txt2svg.pl33
-rw-r--r--share/extensions/wavy.inx17
-rwxr-xr-xshare/extensions/wavy.py133
-rw-r--r--share/extensions/whirl.inx16
-rw-r--r--share/extensions/whirl.py65
-rw-r--r--share/extensions/wmf_input.inx14
85 files changed, 4755 insertions, 0 deletions
diff --git a/share/extensions/.cvsignore b/share/extensions/.cvsignore
new file mode 100644
index 000000000..3dda72986
--- /dev/null
+++ b/share/extensions/.cvsignore
@@ -0,0 +1,2 @@
+Makefile.in
+Makefile
diff --git a/share/extensions/Makefile.am b/share/extensions/Makefile.am
new file mode 100644
index 000000000..2e32312ca
--- /dev/null
+++ b/share/extensions/Makefile.am
@@ -0,0 +1,99 @@
+
+extensiondir = $(datadir)/inkscape/extensions
+
+otherstuffdir = $(datadir)/inkscape/extensions
+
+moduledir = $(datadir)/inkscape/extensions
+
+extensions = \
+ ill2svg.pl \
+ SpSVG.pm \
+ svg_dropshadow \
+ dia2svg.sh \
+ sk2svg.sh \
+ ps2epsi.sh \
+ ps2pdf.sh \
+ txt2svg.pl \
+ inkscape-shadow.sh \
+ inkscape-shadow-white.sh \
+ embed_raster_in_svg.pl \
+ bezmisc.py \
+ cubicsuperpath.py \
+ dots.py \
+ ffgeom.py \
+ ffproc.py \
+ ffscale.py \
+ fretfind.py \
+ handles.py \
+ inkex.py \
+ interp.py \
+ kochify.py \
+ kochify_load.py \
+ lindenmayer.py \
+ motion.py \
+ pturtle.py \
+ radiusrand.py \
+ rtree.py \
+ simplepath.py \
+ simplestyle.py \
+ straightseg.py \
+ wavy.py \
+ whirl.py \
+ addnodes.py \
+ summersnight.py \
+ ps2dxf.sh \
+ embedimage.py \
+ extractimage.py \
+ svg_and_media_zip_output.py
+
+otherstuff =
+
+modules = \
+ svgz_input.inx \
+ svgz_output.inx \
+ ai_input.inx \
+ ai_output.inx \
+ ps_input.inx \
+ eps_input.inx \
+ sk_input.inx \
+ wmf_input.inx \
+ dropshadow.inx \
+ dia.inx \
+ epsi_output.inx \
+ pdf_output.inx \
+ txt2svg.inx \
+ dots.inx \
+ ffmet.inx \
+ ffms.inx \
+ ffss.inx \
+ ffset.inx \
+ handles.inx \
+ interp.inx \
+ kochify.inx \
+ kochify_load.inx \
+ lindenmayer.inx \
+ motion.inx \
+ radiusrand.inx \
+ rtree.inx \
+ straightseg.inx \
+ wavy.inx \
+ whirl.inx \
+ addnodes.inx \
+ summersnight.inx \
+ dxf_output.inx \
+ dxf_input.inx \
+ embedimage.inx \
+ extractimage.inx \
+ svg_and_media_zip_output.inx
+
+extension_SCRIPTS = \
+ $(extensions)
+
+otherstuff_DATA = \
+ $(otherstuff)
+
+module_DATA = \
+ $(modules)
+
+EXTRA_DIST = $(extension_SCRIPTS) $(otherstuff_DATA) $(module_DATA)
+
diff --git a/share/extensions/README b/share/extensions/README
new file mode 100644
index 000000000..74b4f7bc1
--- /dev/null
+++ b/share/extensions/README
@@ -0,0 +1,12 @@
+This folder contains Inkscape extensions, i.e. the scripts
+that Inkscape uses to perform some specialized functions.
+
+Each *.inx file contains a description of an extension,
+listing its name, description, prerequisites, etc. These
+files are read by Inkscape on launch. Other files are
+the scripts themselves (Perl, Python, and Ruby are
+supported, as well as shell scripts).
+
+Some of the scripts here are not (yet) hooked up to Inkscape
+in any way but can be used from the command line (e.g.
+inkscape-shadow*).
diff --git a/share/extensions/SpSVG.pm b/share/extensions/SpSVG.pm
new file mode 100644
index 000000000..b3f7ed09f
--- /dev/null
+++ b/share/extensions/SpSVG.pm
@@ -0,0 +1,351 @@
+#!/usr/bin/perl -w
+#
+# SpSVG
+#
+# Perl module for sodipodi extensions
+#
+# This is a temporary hack that provides the following:
+# * Some standard getopts (help, i/o, ids)
+# * A way to exit that produces the error codes outlined in
+# the extension specs (SpSVG::error)
+# * A method that takes a function as its arguments and passes
+# each specified element ('--id=foo --id=bar', 'ids=fooz,baaz',
+# and so forth) as plain text to the function. The function is
+# expected to return the processed version of this text.
+#
+# TODO:
+#
+# * Write POD
+# * Exit with a friendly message if XML::XQL isn't installed
+# * Decide how to implement the module interface
+# * Move from XML::XQL to SVG/SVG::Parser (see below)
+# * Make the process method more efficient (again, see below)
+#
+# Authors: Daniel Goude (goude@dtek.chalmers.se)
+#
+
+package SpSVG; # Think of a better name
+use strict;
+#use Carp;
+use Exporter;
+use Getopt::Long;
+#use Data::Dumper; # For debugging
+
+# From the SVG.pm documentation (actually
+# http://roasp.com/tutorial/tutorial6.shtml):
+#
+# > Currently, version 2.0 of SVG.pm does not internally support DOM
+# > traversiong functionality such as getting the children,siblings,or
+# > parent of an element, so the interaction capability between SVG::Parser
+# > and SVG is limited to manipulations of a known image. The next version
+# > of SVG will support all these and more key functions which will make
+# > SVG::Parser extremely useful.
+#
+# I plan to replace the /XML::XQL(::DOM)?/ code as soon as this is
+# fixed.
+
+#use SVG;
+#use SVG::Parser;
+
+use XML::XQL;
+use XML::XQL::DOM;
+
+use vars qw(@ISA @EXPORT $VERSION);
+
+$VERSION = 1.02; # fixme: use SpSVG 1.01 doesn't raise exception.
+@ISA = qw(Exporter);
+
+# Symbols
+@EXPORT = qw(
+
+);
+
+sub new {
+ my $self = {
+ status => make_status(),
+ name => '', # Name of script
+ usage => '', # Usage string
+ opt_help => [], # Used for --help
+
+ ids => [], # Array of ids that will be iterated over
+ # in process()
+ svg => '', # SVG document object
+
+ };
+ bless $self;
+}
+
+sub parse {
+ my $self = shift;
+
+ my $infile = $self->{'opts'}->{'file'};
+
+ my $xml;
+ {
+ local $/=undef;
+ if ($infile) {
+ open (IN, $infile) or
+ $self->error('IO_ERR', "Can't open $infile: $!\n");
+ $xml = <IN>;
+ close IN or
+ $self->error('IO_ERR', "Can't close $infile: $!\n");
+ } else {
+ $xml = <>;
+ }
+ }
+
+
+ $self->{'parser'} = new XML::DOM::Parser;
+ my $parser = $self->{'parser'};
+ my $svg = $parser->parse($xml) ||
+ $self->error('INPUT_ERR', "Couldn't parse input: $!.");
+ $self->{'svg'} = $svg;
+}
+
+# Return SVG document as a string
+sub get {
+ my $self = shift;
+ my $string = $self->{'svg'}->toString;
+
+}
+
+# Print to $outfile|STDOUT
+sub dump {
+ my $self = shift;
+ my $outfile = $self->{'opts'}->{'output'};
+ if ($outfile) {
+ open(OUT, ">$outfile") or
+ $self->error('IO_ERR', "Can't open $outfile for writing: $!\n");
+ print OUT $self->get;
+ close OUT or $self->error('IO_ERR', "Can't close $outfile: $!\n");
+ } else {
+ print $self->get;
+ }
+}
+
+sub process_ids {
+ my $self = shift;
+ my $func = shift;
+
+ my @ids = @{$self->{'ids'}};
+
+ # Apply a user supplied function to each id
+ foreach my $id (@ids) {
+ my $svg = $self->{'svg'};
+ #warn "ID: $id\n";
+ my @nodes = $svg->xql("//*[\@id = '$id']") or
+ $self->error('NOOP_ERR', "Couldn't find element $id.");
+ my $node = shift @nodes; # Ids are unique
+ # fixme: Add more checking.
+
+ # Call the user function on the node identified by $id
+ my $new_node = $func->($node->toString);
+
+ # Replace the comment with user generated SVG
+ my $parent = $node->getParentNode;
+ my $comment = $svg->createComment('SpSVG');
+ $parent->replaceChild($comment, $node);
+ my $output = $self->{'svg'}->toString;
+ $output =~ s/<!--SpSVG-->/$new_node/;
+
+ # Here the whole (new) document is parsed. Probably VERY inefficient,
+ # but at least you get syntax checking for free..
+ $self->{'svg'} = $self->{'parser'}->parse($output);
+ #print $self->{'svg'}->toString;
+ }
+
+
+}
+
+# Exit status codes
+sub make_status {
+ my $self = shift;
+ my %status = (
+ 0 => ["SUCCESS", "Extension exited gracefully"],
+ 1 => ["GEN_FAIL", "General failure"],
+ 2 => ["MEM_ERR", "Memory error"],
+ 3 => ["IO_ERR", "File I/O error"],
+ 4 => ["MATH_ERR", "Math error"],
+ 5 => ["INPUT_ERR", "Input not understood (not valid SVG)"],
+ 6 => ["NOOP_ERR", "Could not operate on any objects in this " .
+ "data stream"],
+ 7 => ["ARG_ERR", "Incorrect script arguments"]
+ );
+
+ # Generate error subs dynamically
+ foreach my $exit_code (sort keys %status) {
+ eval "sub $status{$exit_code}[0] { $exit_code; }";
+ die $@ if $@;
+ }
+ return \%status;
+
+}
+
+# Create an option array suitable for Getopt::Long
+sub make_opt_vals {
+ my $self = shift;
+ my @opt_desc = @_;
+ my @opt_vals;
+ my @opt_help = @{$self->{'opt_help'}};
+ foreach (@opt_desc) {
+ my %h = %$_;
+ foreach my $key (keys %h) {
+ #print "Key : $h{$key}\n";
+ if ($key eq 'opt') {
+ push @opt_vals, $h{'opt'};
+ } elsif ($key eq 'desc') {
+ my $option = $h{'opt'};
+ $option =~ s/([^=]+)=.+/$1/;
+ $option =~ s/([^|]+)/(length "$1" > 1 ? '--' : '-') . "$1"/eg;
+ push @opt_help, [$option, $h{'desc'}];
+ }
+ }
+ }
+ $self->{'opt_help'} = \@opt_help;
+ return @opt_vals;
+}
+
+# Parse command line options
+sub get_opts {
+ my $self = shift;
+ my @user_opt_desc = @_;
+
+ my @opt_desc = (
+ {
+ opt => 'help|h',
+ desc => 'Display this help and exit.',
+ },
+
+ {
+ opt => 'version|v',
+ desc => 'Print version and exit.',
+ },
+
+ {
+ opt => 'file|F=s',
+ desc => 'Input file (default: STDIN).',
+ },
+
+ {
+ opt => 'output|o=s',
+ desc => 'Output file (default: STDOUT).',
+ },
+
+ {
+ opt => 'id=s@',
+ desc => 'svg id to operate on (can be multiple).',
+ },
+
+ {
+ opt => 'ids=s',
+ desc => 'Comma-separated list of svg ids to operate on.',
+ },
+ );
+
+ # Create option arrays for Getopt::Long
+ my @opt_vals = $self->make_opt_vals(@opt_desc);
+ my @user_opt_vals = $self->make_opt_vals(@user_opt_desc);
+
+ # Append user options
+ foreach (@user_opt_vals) {
+ push @opt_vals, $_;
+ }
+
+ # Where the parsed options are stored
+ my %opts;
+
+ #exit 0;
+
+ # Parse all options
+ GetOptions(\%opts, @opt_vals) or usage();
+
+ # Handle comma-separated 'ids=foo,bar'
+ my @ids = @{$opts{'id'}} if $opts{'id'};
+ if (exists $opts{'ids'} && $opts{'ids'} =~ /[\w\d_]+(,[\w\d_]+)*/) {
+ push (@ids, split(/,/, $opts{'ids'}));
+ }
+
+ # Display usage etc. (and exit)
+ exists $opts{'version'} && $self->version();
+ exists $opts{'help'} && $self->usage();
+
+ # Save id values for later processing
+ $self->{'ids'} = \@ids;
+
+ # Save options
+ $self->{'opts'} = \%opts;
+
+ # Return the options to script
+ return %opts;
+}
+
+# Exit with named exit status
+sub error {
+ my $self = shift;
+ my $error_name = shift;
+ my $script_error_msg = shift || '';
+
+ my %status = %{$self->{'status'}};
+
+ foreach (keys %status) {
+ if ($status{$_}[0] eq $error_name) {
+ $! = $_; # Set exit status
+
+ # Commented out; let sodipodi handle the error code instead
+ #my $msg = ($status{$_}->[1] . ": $script_error_msg");
+
+ my $msg = "$script_error_msg";
+ die $msg;
+ }
+ }
+
+ # Will not be reached unless an improper error_name is given
+ $! = 255; # Exit status
+ warn "Illegal error code '$error_name' called from script\n";
+}
+
+# Some accessor methods
+sub set_usage {
+ my $self = shift;
+ my $usage = shift || die "No usage string supplied!\n";
+ $self->{'usage'} = $usage;
+}
+
+sub set_name {
+ my $self = shift;
+ my $name = shift || die "No script name supplied!\n";
+ $self->{'name'} = $name;
+}
+
+# Print usage and exit
+sub usage {
+ my $self = shift;
+ print "Usage: $self->{'name'} OPTIONS FILE\n";
+ print $self->{'usage'};
+
+ my @opt_help = @{$self->{'opt_help'}};
+ foreach (@opt_help) {
+ print pad($_->[0]) . $_->[1] . "\n";
+ }
+
+ exit ARG_ERR();
+}
+
+sub pad {
+ my $string = shift;
+ my $width = '20';
+ return $string . ' ' x ($width - length($string));
+}
+
+# Print version
+sub version {
+ print "Uses SpSVG version $VERSION\n";
+ exit ARG_ERR();
+}
+
+# End of module; return something true
+1;
+
+__END__
+
+DOCUMENTATION HERE
diff --git a/share/extensions/addnodes.inx b/share/extensions/addnodes.inx
new file mode 100644
index 000000000..0ce315857
--- /dev/null
+++ b/share/extensions/addnodes.inx
@@ -0,0 +1,13 @@
+<inkscape-extension>
+ <_name>Add Nodes</_name>
+ <id>org.ekips.filter.addnodes</id>
+ <dependency type="executable" location="extensions">addnodes.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="max" type="float" min="0.0" max="10000.0" _gui-text="Maximum Segment Length">10.0</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">addnodes.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/addnodes.py b/share/extensions/addnodes.py
new file mode 100644
index 000000000..d9814e066
--- /dev/null
+++ b/share/extensions/addnodes.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import inkex, cubicsuperpath, simplestyle, copy, math, re, bezmisc
+
+def numsegs(csp):
+ return sum([len(p)-1 for p in csp])
+def tpoint((x1,y1), (x2,y2), t = 0.5):
+ return [x1+t*(x2-x1),y1+t*(y2-y1)]
+def cspbezsplit(sp1, sp2, t = 0.5):
+ m1=tpoint(sp1[1],sp1[2],t)
+ m2=tpoint(sp1[2],sp2[0],t)
+ m3=tpoint(sp2[0],sp2[1],t)
+ m4=tpoint(m1,m2,t)
+ m5=tpoint(m2,m3,t)
+ m=tpoint(m4,m5,t)
+ return [[sp1[0][:],sp1[1][:],m1], [m4,m,m5], [m3,sp2[1][:],sp2[2][:]]]
+def cspbezsplitatlength(sp1, sp2, l = 0.5, tolerance = 0.001):
+ bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+ t = bezmisc.beziertatlength(bez, l, tolerance)
+ return cspbezsplit(sp1, sp2, t)
+def cspseglength(sp1,sp2, tolerance = 0.001):
+ bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+ return bezmisc.bezierlength(bez, tolerance)
+def csplength(csp):
+ total = 0
+ lengths = []
+ for sp in csp:
+ lengths.append([])
+ for i in xrange(1,len(sp)):
+ l = cspseglength(sp[i-1],sp[i])
+ lengths[-1].append(l)
+ total += l
+ return lengths, total
+def numlengths(csplen):
+ retval = 0
+ for sp in csplen:
+ for l in sp:
+ if l > 0:
+ retval += 1
+ return retval
+
+class SplitIt(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-m", "--max",
+ action="store", type="float",
+ dest="max", default=0.0,
+ help="maximum segment length")
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ d = node.attributes.getNamedItem('d')
+ p = cubicsuperpath.parsePath(d.value)
+
+ #lens, total = csplength(p)
+ #avg = total/numlengths(lens)
+ #inkex.debug("average segment length: %s" % avg)
+
+ new = []
+ for sub in p:
+ new.append([sub[0][:]])
+ i = 1
+ while i <= len(sub)-1:
+ length = cspseglength(new[-1][-1], sub[i])
+ if length > self.options.max:
+ splits = math.ceil(length/self.options.max)
+ for s in xrange(int(splits),1,-1):
+ new[-1][-1], next, sub[i] = cspbezsplitatlength(new[-1][-1], sub[i], 1.0/s)
+ new[-1].append(next[:])
+ new[-1].append(sub[i])
+ i+=1
+
+ d.value = cubicsuperpath.formatPath(new)
+
+e = SplitIt()
+e.affect()
diff --git a/share/extensions/ai_input.inx b/share/extensions/ai_input.inx
new file mode 100644
index 000000000..a6a5794d5
--- /dev/null
+++ b/share/extensions/ai_input.inx
@@ -0,0 +1,16 @@
+<inkscape-extension>
+ <_name>AI Input</_name>
+ <id>org.inkscape.input.ai</id>
+ <dependency type="executable" location="path">perl</dependency>
+ <dependency type="executable" location="extensions">ill2svg.pl</dependency>
+ <input>
+ <extension>.ai</extension>
+ <mimetype>image/x-adobe-illustrator</mimetype>
+ <_filetypename>Adobe Illustrator (*.ai)</_filetypename>
+ <_filetypetooltip>Open files saved with Adobe Illustrator</_filetypetooltip>
+ <output_extension>org.inkscape.output.ai</output_extension>
+ </input>
+ <script>
+ <command reldir="extensions" interpreter="perl">ill2svg.pl</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/ai_output.inx b/share/extensions/ai_output.inx
new file mode 100644
index 000000000..ebed2a194
--- /dev/null
+++ b/share/extensions/ai_output.inx
@@ -0,0 +1,16 @@
+<inkscape-extension>
+ <_name>AI Output</_name>
+ <id>org.inkscape.output.ai</id>
+ <dependency type="executable" location="path">gs</dependency>
+ <dependency type="extension">org.inkscape.output.ps</dependency>
+ <output>
+ <extension>.ai</extension>
+ <mimetype>image/x-adobe-illustrator</mimetype>
+ <_filetypename>Adobe Illustrator (*.ai)</_filetypename>
+ <_filetypetooltip>Write Adobe Illustrator</_filetypetooltip>
+ </output>
+ <script>
+ <command reldir="path">gs -q -dNODISPLAY -dSAFER ps2ai.ps</command>
+ <helper_extension>org.inkscape.output.ps</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/bezmisc.py b/share/extensions/bezmisc.py
new file mode 100755
index 000000000..7efafa3ea
--- /dev/null
+++ b/share/extensions/bezmisc.py
@@ -0,0 +1,250 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+
+import math, cmath
+
+def rootWrapper(a,b,c,d):
+ if a:
+ #TODO: find a new cubic solver and put it here
+ #return solveCubicMonic(b/a,c/a,d/a)
+ return ()
+ elif b:
+ det=c**2.0-4.0*b*d
+ if det:
+ return (-c+cmath.sqrt(det))/(2.0*b),(-c-cmath.sqrt(det))/(2.0*b)
+ else:
+ return -c/(2.0*b),
+ elif c:
+ return 1.0*(-d/c),
+ return ()
+
+def bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))):
+ #parametric bezier
+ x0=bx0
+ y0=by0
+ cx=3*(bx1-x0)
+ bx=3*(bx2-bx1)-cx
+ ax=bx3-x0-cx-bx
+ cy=3*(by1-y0)
+ by=3*(by2-by1)-cy
+ ay=by3-y0-cy-by
+
+ return ax,ay,bx,by,cx,cy,x0,y0
+ #ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+
+def linebezierintersect(((lx1,ly1),(lx2,ly2)),((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3))):
+ #parametric line
+ dd=lx1
+ cc=lx2-lx1
+ bb=ly1
+ aa=ly2-ly1
+
+ if aa:
+ coef1=cc/aa
+ coef2=1
+ else:
+ coef1=1
+ coef2=aa/cc
+
+ ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+ #cubic intersection coefficients
+ a=coef1*ay-coef2*ax
+ b=coef1*by-coef2*bx
+ c=coef1*cy-coef2*cx
+ d=coef1*(y0-bb)-coef2*(x0-dd)
+
+ roots = rootWrapper(a,b,c,d)
+ retval = []
+ for i in roots:
+ if type(i) is complex and i.imag==0:
+ i = i.real
+ if type(i) is not complex and 0<=i<=1:
+ retval.append(i)
+ return retval
+
+def bezierpointatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
+ ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+ x=ax*(t**3)+bx*(t**2)+cx*t+x0
+ y=ay*(t**3)+by*(t**2)+cy*t+y0
+ return x,y
+
+def bezierslopeatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
+ ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+ dx=3*ax*(t**2)+2*bx*t+cx
+ dy=3*ay*(t**2)+2*by*t+cy
+ return dx,dy
+
+def beziertatslope(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),(dy,dx)):
+ ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+ #quadratic coefficents of slope formula
+ if dx:
+ slope = 1.0*(dy/dx)
+ a=3*ay-3*ax*slope
+ b=2*by-2*bx*slope
+ c=cy-cx*slope
+ elif dy:
+ slope = 1.0*(dx/dy)
+ a=3*ax-3*ay*slope
+ b=2*bx-2*by*slope
+ c=cx-cy*slope
+ else:
+ return []
+
+ roots = rootWrapper(0,a,b,c)
+ retval = []
+ for i in roots:
+ if type(i) is complex and i.imag==0:
+ i = i.real
+ if type(i) is not complex and 0<=i<=1:
+ retval.append(i)
+ return retval
+
+def tpoint((x1,y1),(x2,y2),t):
+ return x1+t*(x2-x1),y1+t*(y2-y1)
+def beziersplitatt(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)),t):
+ m1=tpoint((bx0,by0),(bx1,by1),t)
+ m2=tpoint((bx1,by1),(bx2,by2),t)
+ m3=tpoint((bx2,by2),(bx3,by3),t)
+ m4=tpoint(m1,m2,t)
+ m5=tpoint(m2,m3,t)
+ m=tpoint(m4,m5,t)
+
+ return ((bx0,by0),m1,m4,m),(m,m5,m3,(bx3,by3))
+
+'''
+Approximating the arc length of a bezier curve
+according to <http://www.cit.gu.edu.au/~anthony/info/graphics/bezier.curves>
+
+if:
+ L1 = |P0 P1| +|P1 P2| +|P2 P3|
+ L0 = |P0 P3|
+then:
+ L = 1/2*L0 + 1/2*L1
+ ERR = L1-L0
+ERR approaches 0 as the number of subdivisions (m) increases
+ 2^-4m
+
+Reference:
+Jens Gravesen <gravesen@mat.dth.dk>
+"Adaptive subdivision and the length of Bezier curves"
+mat-report no. 1992-10, Mathematical Institute, The Technical
+University of Denmark.
+'''
+def pointdistance((x1,y1),(x2,y2)):
+ return math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2))
+def Gravesen_addifclose(b, len, error = 0.001):
+ box = 0
+ for i in range(1,4):
+ box += pointdistance(b[i-1], b[i])
+ chord = pointdistance(b[0], b[3])
+ if (box - chord) > error:
+ first, second = beziersplitatt(b, 0.5)
+ Gravesen_addifclose(first, len, error)
+ Gravesen_addifclose(second, len, error)
+ else:
+ len[0] += (box / 2.0) + (chord / 2.0)
+def bezierlengthGravesen(b, error = 0.001):
+ len = [0]
+ Gravesen_addifclose(b, len, error)
+ return len[0]
+
+# balf = Bezier Arc Length Function
+balfax,balfbx,balfcx,balfay,balfby,balfcy = 0,0,0,0,0,0
+def balf(t):
+ retval = (balfax*(t**2) + balfbx*t + balfcx)**2 + (balfay*(t**2) + balfby*t + balfcy)**2
+ return math.sqrt(retval)
+
+def Simpson(f, a, b, n_limit, tolerance):
+ n = 2
+ multiplier = (b - a)/6.0
+ endsum = f(a) + f(b)
+ interval = (b - a)/2.0
+ asum = 0.0
+ bsum = f(a + interval)
+ est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
+ est0 = 2.0 * est1
+ #print multiplier, endsum, interval, asum, bsum, est1, est0
+ while n < n_limit and abs(est1 - est0) > tolerance:
+ n *= 2
+ multiplier /= 2.0
+ interval /= 2.0
+ asum += bsum
+ bsum = 0.0
+ est0 = est1
+ for i in xrange(1, n, 2):
+ bsum += f(a + (i * interval))
+ est1 = multiplier * (endsum + (2.0 * asum) + (4.0 * bsum))
+ #print multiplier, endsum, interval, asum, bsum, est1, est0
+ return est1
+
+def bezierlengthSimpson(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), tolerance = 0.001):
+ global balfax,balfbx,balfcx,balfay,balfby,balfcy
+ ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+ balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy
+ return Simpson(balf, 0.0, 1.0, 4096, tolerance)
+
+def beziertatlength(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)), l = 0.5, tolerance = 0.001):
+ global balfax,balfbx,balfcx,balfay,balfby,balfcy
+ ax,ay,bx,by,cx,cy,x0,y0=bezierparameterize(((bx0,by0),(bx1,by1),(bx2,by2),(bx3,by3)))
+ balfax,balfbx,balfcx,balfay,balfby,balfcy = 3*ax,2*bx,cx,3*ay,2*by,cy
+ t = 1.0
+ tdiv = t
+ curlen = Simpson(balf, 0.0, t, 4096, tolerance)
+ targetlen = l * curlen
+ diff = curlen - targetlen
+ while abs(diff) > tolerance:
+ tdiv /= 2.0
+ if diff < 0:
+ t += tdiv
+ else:
+ t -= tdiv
+ curlen = Simpson(balf, 0.0, t, 4096, tolerance)
+ diff = curlen - targetlen
+ return t
+
+#default bezier length method
+bezierlength = bezierlengthSimpson
+
+if __name__ == '__main__':
+ import timing
+ #print linebezierintersect(((,),(,)),((,),(,),(,),(,)))
+ #print linebezierintersect(((0,1),(0,-1)),((-1,0),(-.5,0),(.5,0),(1,0)))
+ tol = 0.00000001
+ curves = [((0,0),(1,5),(4,5),(5,5)),
+ ((0,0),(0,0),(5,0),(10,0)),
+ ((0,0),(0,0),(5,1),(10,0)),
+ ((-10,0),(0,0),(10,0),(10,10)),
+ ((15,10),(0,0),(10,0),(-5,10))]
+ '''
+ for curve in curves:
+ timing.start()
+ g = bezierlengthGravesen(curve,tol)
+ timing.finish()
+ gt = timing.micro()
+
+ timing.start()
+ s = bezierlengthSimpson(curve,tol)
+ timing.finish()
+ st = timing.micro()
+
+ print g, gt
+ print s, st
+ '''
+ for curve in curves:
+ print beziertatlength(curve,0.5)
diff --git a/share/extensions/bluredge.inx b/share/extensions/bluredge.inx
new file mode 100644
index 000000000..567efbc79
--- /dev/null
+++ b/share/extensions/bluredge.inx
@@ -0,0 +1,13 @@
+<inkscape-extension>
+ <_name>Blur Edge</_name>
+ <id>org.inkscape.effect.bluredge</id>
+ <dependency type="plugin" location="plugins">bluredge</dependency>
+ <param name="blur-width" type="float">1.0</param>
+ <param name="num-steps" type="int">11</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <plugin>
+ <name>bluredge</name>
+ </plugin>
+</inkscape-extension>
diff --git a/share/extensions/cubicsuperpath.py b/share/extensions/cubicsuperpath.py
new file mode 100755
index 000000000..fdd1afc59
--- /dev/null
+++ b/share/extensions/cubicsuperpath.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python
+"""
+cubicsuperpath.py
+
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""
+import simplepath
+
+def CubicSuperPath(simplepath):
+ csp = []
+ subpath = -1
+ subpathstart = []
+ last = []
+ lastctrl = []
+ for s in simplepath:
+ cmd, params = s
+ if cmd == 'M':
+ if last:
+ csp[subpath].append([lastctrl[:],last[:],last[:]])
+ subpath += 1
+ csp.append([])
+ subpathstart = params[:]
+ last = params[:]
+ lastctrl = params[:]
+ elif cmd == 'L':
+ csp[subpath].append([lastctrl[:],last[:],last[:]])
+ last = params[:]
+ lastctrl = params[:]
+ elif cmd == 'C':
+ csp[subpath].append([lastctrl[:],last[:],params[:2]])
+ last = params[-2:]
+ lastctrl = params[2:4]
+ elif cmd == 'Q':
+ #TODO: convert to cubic
+ csp[subpath].append([lastctrl[:],last[:],last[:]])
+ last = params[-2:]
+ lastctrl = params[-2:]
+ elif cmd == 'A':
+ #TODO: convert to cubics
+ csp[subpath].append([lastctrl[:],last[:],last[:]])
+ last = params[-2:]
+ lastctrl = params[-2:]
+ elif cmd == 'Z':
+ csp[subpath].append([lastctrl[:],last[:],last[:]])
+ last = subpathstart[:]
+ lastctrl = subpathstart[:]
+ #append final superpoint
+ csp[subpath].append([lastctrl[:],last[:],last[:]])
+ return csp
+
+def unCubicSuperPath(csp):
+ a = []
+ for subpath in csp:
+ if subpath:
+ a.append(['M',subpath[0][1][:]])
+ for i in range(1,len(subpath)):
+ a.append(['C',subpath[i-1][2][:] + subpath[i][0][:] + subpath[i][1][:]])
+ return a
+
+def parsePath(d):
+ return CubicSuperPath(simplepath.parsePath(d))
+
+def formatPath(p):
+ return simplepath.formatPath(unCubicSuperPath(p))
+
+
+
+
diff --git a/share/extensions/dia.inx b/share/extensions/dia.inx
new file mode 100644
index 000000000..3e9fa244c
--- /dev/null
+++ b/share/extensions/dia.inx
@@ -0,0 +1,15 @@
+<inkscape-extension>
+ <_name>Dia Input</_name>
+ <id>org.inkscape.input.dia</id>
+ <dependency type="executable" location="extensions" _description="The dia2svg.sh script should be installed with your Inkscape distribution. If you do not have it, there is likely to be something wrong with your Inkscape installation.">dia2svg.sh</dependency>
+ <dependency type="executable" _description="In order to import Dia files, Dia itself must be installed. You can get Dia at http://www.gnome.org/projects/dia/">dia</dependency>
+ <input>
+ <extension>.dia</extension>
+ <mimetype>application/x-dia</mimetype>
+ <_filetypename>Dia Diagram (*.dia)</_filetypename>
+ <_filetypetooltip>A diagram created with the program Dia</_filetypetooltip>
+ </input>
+ <script>
+ <command reldir="extensions">dia2svg.sh</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/dia2svg.sh b/share/extensions/dia2svg.sh
new file mode 100755
index 000000000..48403d0b5
--- /dev/null
+++ b/share/extensions/dia2svg.sh
@@ -0,0 +1,13 @@
+#! /bin/sh
+
+rc=0
+
+# dia version 0.93 (the only version I've tested) allows `--export=-', but then
+# ruins it by writing other cruft to stdout. So we'll have to use a temp file.
+TMPDIR="${TMPDIR-/tmp}"
+TEMPFILENAME=`mktemp 2>/dev/null || echo "$TMPDIR/tmpdia$$.svg"`
+dia -n --export="${TEMPFILENAME}" --export-to-format=svg "$1" > /dev/null 2>&1 || rc=1
+
+cat < "${TEMPFILENAME}" || rc=1
+rm -f "${TEMPFILENAME}"
+exit $rc
diff --git a/share/extensions/dots.inx b/share/extensions/dots.inx
new file mode 100644
index 000000000..50e37ff4c
--- /dev/null
+++ b/share/extensions/dots.inx
@@ -0,0 +1,14 @@
+<inkscape-extension>
+ <_name>Connect the Dots</_name>
+ <id>org.ekips.filter.dots</id>
+ <dependency type="executable" location="extensions">dots.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="fontsize" type="string" _gui-text="Font Size">20</param>
+ <param name="dotsize" type="string" _gui-text="Dot Size">10px</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">dots.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/dots.py b/share/extensions/dots.py
new file mode 100755
index 000000000..2e24ef2b1
--- /dev/null
+++ b/share/extensions/dots.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import inkex, simplestyle, simplepath
+
+class Dots(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-d", "--dotsize",
+ action="store", type="string",
+ dest="dotsize", default="10px",
+ help="Size of the dots placed at path nodes")
+ self.OptionParser.add_option("-f", "--fontsize",
+ action="store", type="string",
+ dest="fontsize", default="20",
+ help="Size of node label numbers")
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ self.group = self.document.createElement('svg:g')
+ node.parentNode.appendChild(self.group)
+ new = self.document.createElement('svg:path')
+
+ try:
+ t = node.attributes.getNamedItem('transform').value
+ self.group.setAttribute('transform', t)
+ except AttributeError:
+ pass
+
+ s = simplestyle.parseStyle(node.attributes.getNamedItem('style').value)
+ s['stroke-linecap']='round'
+ s['stroke-width']=self.options.dotsize
+ new.setAttribute('style', simplestyle.formatStyle(s))
+
+ a =[]
+ p = simplepath.parsePath(node.attributes.getNamedItem('d').value)
+ num = 1
+ for cmd,params in p:
+ if cmd != 'Z':
+ a.append(['M',params[-2:]])
+ a.append(['L',params[-2:]])
+ self.addText(self.group,params[-2],params[-1],num)
+ num += 1
+ new.setAttribute('d', simplepath.formatPath(a))
+ self.group.appendChild(new)
+ node.parentNode.removeChild(node)
+
+
+ def addText(self,node,x,y,text):
+ new = self.document.createElement('svg:text')
+ s = {'font-size': self.options.fontsize, 'fill-opacity': '1.0', 'stroke': 'none',
+ 'font-weight': 'normal', 'font-style': 'normal', 'fill': '#000000'}
+ new.setAttribute('style', simplestyle.formatStyle(s))
+ new.setAttribute('x', str(x))
+ new.setAttribute('y', str(y))
+ new.appendChild(self.document.createTextNode(str(text)))
+ node.appendChild(new)
+
+e = Dots()
+e.affect()
diff --git a/share/extensions/dropshadow.inx b/share/extensions/dropshadow.inx
new file mode 100644
index 000000000..42aa077dd
--- /dev/null
+++ b/share/extensions/dropshadow.inx
@@ -0,0 +1,13 @@
+<inkscape-extension>
+ <_name>Dropshadow</_name>
+ <id>org.inkscape.filter.dropshadow</id>
+ <dependency type="executable" location="extensions">svg_dropshadow</dependency>
+ <param name="opacity" _gui-text="Opacity" type="float" min="0.0" max="1.0">0.9</param>
+ <param name="color" _gui-text="Color of shadow" type="string">black</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="perl">svg_dropshadow</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/dxf_input.inx b/share/extensions/dxf_input.inx
new file mode 100644
index 000000000..769c29fdd
--- /dev/null
+++ b/share/extensions/dxf_input.inx
@@ -0,0 +1,16 @@
+<inkscape-extension>
+ <_name>DXF Input</_name>
+ <id>org.inkscape.input.dxf</id>
+ <dependency type="executable" _description="dxf2svg may come with Inkscape, but is also at http://dxf-svg-convert.sourceforge.net/">dxf2svg</dependency>
+ <dependency type="extension">org.inkscape.input.svg</dependency>
+ <input>
+ <extension>.dxf</extension>
+ <mimetype>image/x-svgz</mimetype>
+ <_filetypename>AutoCAD DXF (*.dxf)</_filetypename>
+ <_filetypetooltip>Import AutoCAD's Document Exchange Format</_filetypetooltip>
+ <output_extension>org.inkscape.output.svg</output_extension>
+ </input>
+ <script>
+ <command reldir="path">dxf2svg</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/dxf_output.inx b/share/extensions/dxf_output.inx
new file mode 100644
index 000000000..51dc08bbe
--- /dev/null
+++ b/share/extensions/dxf_output.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>DXF Output</_name>
+ <id>org.inkscape.output.dxf</id>
+ <dependency type="extension">org.inkscape.output.ps</dependency>
+ <dependency type="executable" location="extensions">ps2dxf.sh</dependency>
+ <dependency type="executable" _description="pstoedit must be installed to run see http://www.pstoedit.net/pstoedit">pstoedit</dependency>
+ <output>
+ <extension>.dxf</extension>
+ <mimetype>image/dxf</mimetype>
+ <_filetypename>AutoCAD DXF (*.dxf)</_filetypename>
+ <_filetypetooltip>DXF file written by pstoedit</_filetypetooltip>
+ </output>
+ <script>
+ <command reldir="extensions">ps2dxf.sh</command>
+ <helper_extension>org.inkscape.output.ps</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/embed_raster_in_svg.pl b/share/extensions/embed_raster_in_svg.pl
new file mode 100755
index 000000000..ee3bf295e
--- /dev/null
+++ b/share/extensions/embed_raster_in_svg.pl
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+#----------------------------------------------------------------------------------------------
+# embed_raster_in_svg
+# B. Crowell, crowellXX at lightandmatter.com (replace XX with last two digits of current year)
+# (c) 2004 B. Crowell
+# This program is available under version 2 of the GPL License, http://www.gnu.org/copyleft/gpl.html,
+# or, at your option, under the same license as Perl.
+# For information about this program, scroll down in the source code, or invoke the program
+# without any command-line arguments.
+#----------------------------------------------------------------------------------------------
+
+use strict;
+
+use MIME::Base64;
+use Cwd;
+use File::Basename;
+
+my $info = <<'INFO';
+ usage:
+ embed_raster_in_svg foo.svg
+ Looks through the svg file for raster images that are given as links, rather than
+ being embedded, and embeds them in the file. The links must be local URIs, and if
+ they're relative, they must be relative to the directory where the svg file is located.
+ Starting with Inkscape 0.41, Inkscape can
+ handle embedded raster images as well as ones that are linked to (?).
+ Typical input looks like this:
+ <image
+ xlink:href="beer.jpg"
+ sodipodi:absref="/home/bcrowell/Documents/programming/embed_raster_in_svg/tests/beer.jpg"
+ width="202.50000"
+ height="537.00000"
+ id="image1084"
+ x="281.67480"
+ y="242.58502" />
+ The output should look like this:
+ <image
+ width="202.50000"
+ height="537.00000"
+ id="image1084"
+ x="281.67480"
+ y="242.58502"
+ xlink:href="data:;base64,...
+ The SVG standard only allows the <image> tag to refer to data of type png, jpg, or svg,
+ and this script only handles png or jpg.
+INFO
+
+undef $/; # slurp whole file
+
+my $svg = $ARGV[0];
+
+if (!$svg) {
+ print $info;
+ exit;
+}
+
+die "input file $svg not found" if ! -e $svg;
+die "input file $svg not readable" if ! -r $svg;
+
+open(FILE,"<$svg") or die "error opening file $svg for input, $!";
+my $data = <FILE>;
+close FILE;
+
+chdir dirname(Cwd::realpath($svg)); # so from now on, we can open raster files if they're relative to this directory
+
+
+# The file is now in memory.
+# First we go once through it and (1) find out if there are indeed any links to raster images,
+# (2) rearrange things a little so that the (lengthy) base64 data will be the last attribute of the
+# image tag.
+
+my $n = 0;
+my @raster_files = ();
+$data =~
+ s@\<image\s+((?:(?:\w|\:)+\=\"[^"]*\"\s*)*)xlink\:href=\"([^"]+)\"\s*((?:(?:\w|:)+\=\"[^"]*\"\s*)*)\/>@<imageHOBBITSES $1 $3 ITHURTSUS\"$2\"MYPRECIOUSS />@g;
+while ($data=~m@<imageHOBBITSES\s*(?:(?:(?:\w|\:)+\=\"[^"]*\"\s*)*)ITHURTSUS\"([^"]+)\"MYPRECIOUSS />@g) {
+ my $raster = $1;
+ die "error, raster filename $raster contains a double quote" if $raster=~m/\"/;
+ if (!($raster=~m/data\:\;/)) {
+ ++$n;
+ push @raster_files,$raster;
+ }
+}
+
+if ($n==0) {
+ print "no embedded jpgs found in file $svg\n";
+ exit;
+}
+
+
+# Eliminate sodipodi:absref attributes. If these are present, Inkscape looks for the linked
+# file, and ignores the embedded data. Also, get rid of those nasty hobbitses.
+$data=~s@(<image)HOBBITSES\s*((?:(?:\w|\:)+\=\"[^"]*\"\s*)*)sodipodi\:absref\=\"[^"]+\"\s*((:?(?:\w|\:)+\=\"[^"]*\"\s*)*ITHURTSUS)@$1 $2 $3@g;
+$data=~s@(<image)HOBBITSES@$1@g;
+
+
+# Now embed the data:
+
+foreach my $raster(@raster_files) {
+ die "file $raster not found relative to directory ".getcwd() if ! -e $raster;
+ die "file $raster is nor readable" if ! -r $raster;
+ open(RASTER,"<$raster") or die "error opening file $raster for input, $!";
+ my $raster_data = <RASTER>;
+ close RASTER;
+ my $type = '';
+ $type='image/png' if $raster_data=~m/^\x{89}PNG/;
+ $type='image/jpg' if $raster_data=~m/^\x{ff}\x{d8}/;
+ die "file $raster does not appear to be of type PNG or JPG" unless $type;
+ my $raster_data_base64 = encode_base64($raster_data);
+ ($data =~ s@ITHURTSUS\"$raster\"MYPRECIOUSS@\n xlink:href=\"data\:$type\;base64,$raster_data_base64\"@) or die "error embedding data for $raster";
+ print "embedded raster file $raster, $type\n";
+}
+my $bak = "$svg.bak";
+rename $svg,$bak or die "error renaming file $svg to $bak, $!";
+open(FILE,">$svg") or die "error creating new version of file $svg for output, $!";
+print FILE $data;
+close FILE;
+
+
+
+
+
diff --git a/share/extensions/embedimage.inx b/share/extensions/embedimage.inx
new file mode 100644
index 000000000..221a3f0f6
--- /dev/null
+++ b/share/extensions/embedimage.inx
@@ -0,0 +1,12 @@
+<inkscape-extension>
+ <_name>Embed Images</_name>
+ <id>org.ekips.filter.embedimage</id>
+ <dependency type="executable" location="extensions">embedimage.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">embedimage.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/embedimage.py b/share/extensions/embedimage.py
new file mode 100644
index 000000000..12a0b78b2
--- /dev/null
+++ b/share/extensions/embedimage.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+
+import inkex, os, base64
+
+class MyEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+ def effect(self):
+ ctx = inkex.xml.xpath.Context.Context(self.document,processorNss=inkex.NSS)
+
+ # if there is a selection only embed selected images
+ # otherwise embed all images
+ if (self.options.ids):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'image':
+ self.embedImage(node)
+ else:
+ path = '//image'
+ for node in inkex.xml.xpath.Evaluate(path,self.document, context=ctx):
+ self.embedImage(node)
+ def embedImage(self, node):
+ xlink = node.attributes.getNamedItemNS(inkex.NSS[u'xlink'],'href')
+ if (xlink.value[:4]!='data'):
+ absref=node.attributes.getNamedItemNS(inkex.NSS[u'sodipodi'],'absref')
+ if (os.path.isfile(absref.value)):
+ file = open(absref.value,"rb").read()
+ embed=True
+ if (file[:4]=='\x89PNG'):
+ type='image/png'
+ elif (file[:2]=='\xff\xd8'):
+ type='image/jpg'
+ else:
+ embed=False
+ if (embed):
+ xlink.value = 'data:%s;base64,%s' % (type, base64.encodestring(file))
+ node.removeAttributeNS(inkex.NSS[u'sodipodi'],'absref')
+ else:
+ inkex.debug("%s is not of type image/png or image/jpg" % absref.value)
+ else:
+ inkex.debug("Sorry we could not locate %s" % absref.value)
+e = MyEffect()
+e.affect()
diff --git a/share/extensions/eps_input.inx b/share/extensions/eps_input.inx
new file mode 100644
index 000000000..d732dea8c
--- /dev/null
+++ b/share/extensions/eps_input.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>EPS Input</_name>
+ <id>org.inkscape.input.eps</id>
+ <dependency type="extension">org.inkscape.input.ps</dependency>
+ <dependency type="executable">gs</dependency>
+ <input>
+ <extension>.eps</extension>
+ <mimetype>image/x-encapsulated-postscript</mimetype>
+ <_filetypename>Encapsulated Postscript (*.eps)</_filetypename>
+ <_filetypetooltip>Encapsulated Postscript</_filetypetooltip>
+ <output_extension>org.inkscape.output.eps</output_extension>
+ </input>
+ <script>
+ <command reldir="path">gs -q -sDEVICE=pswrite -sOutputFile=- -dNOPAUSE -dBATCH -dSAFER -dDEVICEWIDTH=250000 -dDEVICEHEIGHT=250000</command>
+ <helper_extension>org.inkscape.input.ps</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/epsi_output.inx b/share/extensions/epsi_output.inx
new file mode 100644
index 000000000..06197dc0a
--- /dev/null
+++ b/share/extensions/epsi_output.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>EPSI Output</_name>
+ <id>org.inkscape.output.epsi</id>
+ <dependency type="extension">org.inkscape.output.ps</dependency>
+ <dependency type="executable" location="extensions">ps2epsi.sh</dependency>
+ <dependency type="executable">ps2epsi</dependency>
+ <output>
+ <extension>.epsi</extension>
+ <mimetype>image/x-encapsulated-postscript</mimetype>
+ <_filetypename>Encapsulated Postscript Interchange (*.epsi)</_filetypename>
+ <_filetypetooltip>Encapsulated Postscript with a thumbnail</_filetypetooltip>
+ </output>
+ <script>
+ <command reldir="extensions">ps2epsi.sh</command>
+ <helper_extension>org.inkscape.output.ps</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/extractimage.inx b/share/extensions/extractimage.inx
new file mode 100644
index 000000000..a167ed15d
--- /dev/null
+++ b/share/extensions/extractimage.inx
@@ -0,0 +1,13 @@
+<inkscape-extension>
+ <_name>Extract One Image</_name>
+ <id>org.ekips.filter.extractimage</id>
+ <dependency type="executable" location="extensions">extractimage.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="filepath" type="string" _gui-text="Path to Save Image">none</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">extractimage.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/extractimage.py b/share/extensions/extractimage.py
new file mode 100644
index 000000000..27936bf80
--- /dev/null
+++ b/share/extensions/extractimage.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+
+import inkex, base64
+
+class MyEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("--filepath",
+ action="store", type="string",
+ dest="filepath", default=None,
+ help="")
+ def effect(self):
+ ctx = inkex.xml.xpath.Context.Context(self.document,processorNss=inkex.NSS)
+
+ # exbed the first embedded image
+ path = self.options.filepath
+ if (path != ''):
+ if (self.options.ids):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'image':
+ xlink = node.attributes.getNamedItemNS(inkex.NSS[u'xlink'],'href')
+ if (xlink.value[:4]=='data'):
+ comma = xlink.value.find(',')
+ if comma>0:
+ data = base64.decodestring(xlink.value[comma:])
+ open(path,'wb').write(data)
+ xlink.value = path
+ else:
+ inkex.debug('Difficulty finding the image data.')
+ break
+
+e = MyEffect()
+e.affect()
diff --git a/share/extensions/ffgeom.py b/share/extensions/ffgeom.py
new file mode 100755
index 000000000..1e363c762
--- /dev/null
+++ b/share/extensions/ffgeom.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+"""
+ ffgeom.py
+ Copyright (C) 2005 Aaron Cyril Spike, aaron@ekips.org
+
+ This file is part of FretFind 2-D.
+
+ FretFind 2-D is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ FretFind 2-D is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with FretFind 2-D; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+import math
+try:
+ NaN = float('NaN')
+except ValueError:
+ PosInf = 1e300000
+ NaN = PosInf/PosInf
+
+class Point:
+ precision = 5
+ def __init__(self, x, y):
+ self.__coordinates = {'x' : float(x), 'y' : float(y)}
+ def __getitem__(self, key):
+ return self.__coordinates[key]
+ def __setitem__(self, key, value):
+ self.__coordinates[key] = float(value)
+ def __repr__(self):
+ return '(%s, %s)' % (round(self['x'],self.precision),round(self['y'],self.precision))
+ def copy(self):
+ return Point(self['x'],self['y'])
+ def translate(self, x, y):
+ self['x'] += x
+ self['y'] += y
+ def move(self, x, y):
+ self['x'] = float(x)
+ self['y'] = float(y)
+
+class Segment:
+ def __init__(self, e0, e1):
+ self.__endpoints = [e0, e1]
+ def __getitem__(self, key):
+ return self.__endpoints[key]
+ def __setitem__(self, key, value):
+ self.__endpoints[key] = value
+ def __repr__(self):
+ return repr(self.__endpoints)
+ def copy(self):
+ return Segment(self[0],self[1])
+ def translate(self, x, y):
+ self[0].translate(x,y)
+ self[1].translate(x,y)
+ def move(self,e0,e1):
+ self[0] = e0
+ self[1] = e1
+ def delta_x(self):
+ return self[1]['x'] - self[0]['x']
+ def delta_y(self):
+ return self[1]['y'] - self[0]['y']
+ #alias functions
+ run = delta_x
+ rise = delta_y
+ def slope(self):
+ if self.delta_x() != 0:
+ return self.delta_x() / self.delta_y()
+ return NaN
+ def intercept(self):
+ if self.delta_x() != 0:
+ return self[1]['y'] - (self[0]['x'] * self.slope())
+ return NaN
+ def distanceToPoint(self, p):
+ len = self.length()
+ if len == 0: return NaN
+ return math.fabs(((self[1]['x'] - self[0]['x']) * (self[0]['y'] - p['y'])) - \
+ ((self[0]['x'] - p['x']) * (self[1]['y'] - self[0]['y']))) / len
+ def angle(self):
+ return math.pi * (math.atan2(self.delta_y(), self.delta_x())) / 180
+ def length(self):
+ return math.sqrt((self.delta_x() ** 2) + (self.delta_y() ** 2))
+ def pointAtLength(self, len):
+ if self.length() == 0: return Point(NaN, NaN)
+ ratio = len / self.length()
+ x = self[0]['x'] + (ratio * self.delta_x())
+ y = self[0]['y'] + (ratio * self.delta_y())
+ return Point(x, y)
+ def pointAtRatio(self, ratio):
+ if self.length() == 0: return Point(NaN, NaN)
+ x = self[0]['x'] + (ratio * self.delta_x())
+ y = self[0]['y'] + (ratio * self.delta_y())
+ return Point(x, y)
+ def createParallel(self, p):
+ return Segment(Point(p['x'] + self.delta_x(), p['y'] + self.delta_y()), p)
+ def intersect(self, s):
+ return intersectSegments(self, s)
+
+def intersectSegments(s1, s2):
+ x1 = s1[0]['x']
+ x2 = s1[1]['x']
+ x3 = s2[0]['x']
+ x4 = s2[1]['x']
+
+ y1 = s1[0]['y']
+ y2 = s1[1]['y']
+ y3 = s2[0]['y']
+ y4 = s2[1]['y']
+
+ denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1))
+ num1 = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3))
+ num2 = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3))
+
+ num = num1
+
+ if denom != 0:
+ x = x1 + ((num / denom) * (x2 - x1))
+ y = y1 + ((num / denom) * (y2 - y1))
+ return Point(x, y)
+ return Point(NaN, NaN)
+
diff --git a/share/extensions/ffmet.inx b/share/extensions/ffmet.inx
new file mode 100644
index 000000000..9cebe2d6a
--- /dev/null
+++ b/share/extensions/ffmet.inx
@@ -0,0 +1,23 @@
+<inkscape-extension>
+ <_name>FretFind Multi Length ET</_name>
+ <id>org.ekips.filter.fretfind.multi.et</id>
+ <dependency type="executable" location="extensions">fretfind.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="firstscalelength" type="float" min="1" max="500" _gui-text="First String Length">25</param>
+ <param name="lastscalelength" type="float" min="1" max="500" _gui-text="Last String Length">26</param>
+ <param name="perpdist" type="float" min="-2" max="2" _gui-text="Perpendicular Distance">0.5</param>
+ <param name="nutwidth" type="float" min="0" max="100" _gui-text="Nut Width">2</param>
+ <param name="bridgewidth" type="float" min="0" max="100" _gui-text="Bridge Width">2.5</param>
+ <param name="etbase" type="float" min="1" max="10" _gui-text="Scale Base (2 for Octave)">2</param>
+ <param name="etroot" type="float" min="0.01" max="1000" _gui-text="Tones in Scale">12</param>
+ <param name="frets" type="int" min="1" max="1000" _gui-text="Number of Frets">24</param>
+ <param name="strings" type="int" min="1" max="60" _gui-text="Number of Strings">6</param>
+ <param name="fbedges" type="float" min="0" max="100" _gui-text="Fretboard Edges">0.0975</param>
+ <param name="pxperunit" type="float" min="0" max="1000" _gui-text="px per Unit">90</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">fretfind.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/ffms.inx b/share/extensions/ffms.inx
new file mode 100644
index 000000000..31ac61ecd
--- /dev/null
+++ b/share/extensions/ffms.inx
@@ -0,0 +1,23 @@
+<inkscape-extension>
+ <_name>FretFind Multi Length Scala</_name>
+ <id>org.ekips.filter.fretfind.multi.scala</id>
+ <dependency type="executable" location="extensions">fretfind.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="firstscalelength" type="float" min="1" max="500" _gui-text="First String Length">25</param>
+ <param name="lastscalelength" type="float" min="1" max="500" _gui-text="Last String Length">26</param>
+ <param name="perpdist" type="float" min="-2" max="2" _gui-text="Perpendicular Distance">0.5</param>
+ <param name="nutwidth" type="float" min="0" max="100" _gui-text="Nut Width">2</param>
+ <param name="bridgewidth" type="float" min="0" max="100" _gui-text="Bridge Width">2.5</param>
+ <param name="scalafile" type="string" _gui-text="Path to Scala *.scl File">none</param>
+ <param name="tuning" type="string" _gui-text="Tuning (scale step for each string seperated by semicolons)">0;0;0;0;0;0</param>
+ <param name="frets" type="int" min="1" max="1000" _gui-text="Number of Frets">24</param>
+ <param name="strings" type="int" min="1" max="60" _gui-text="Number of Strings">6</param>
+ <param name="fbedges" type="float" min="0" max="100" _gui-text="Fretboard Edges">0.0975</param>
+ <param name="pxperunit" type="float" min="0" max="1000" _gui-text="px per Unit">90</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">fretfind.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/ffproc.py b/share/extensions/ffproc.py
new file mode 100755
index 000000000..7eb6f2dd1
--- /dev/null
+++ b/share/extensions/ffproc.py
@@ -0,0 +1,194 @@
+#!/usr/bin/env python
+'''
+ Copyright (C) 2004 Aaron Cyril Spike
+
+ This file is part of FretFind 2-D.
+
+ FretFind 2-D is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ FretFind 2-D is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with FretFind 2-D; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import sys
+from ffgeom import *
+threshold=0.0000000001
+
+def FindFrets(strings, meta, scale, tuning, numfrets):
+ scale = scale['steps']
+
+ #if the string ends don't fall on the nut and bridge
+ #don't look for partial frets.
+ numStrings = len(strings)
+ doPartials = True
+ parallelFrets = True
+
+ nut = Segment(strings[0][0],strings[-1][0])
+ bridge = Segment(strings[0][1],strings[-1][1])
+ midline = Segment(
+ Point((nut[1]['x']+nut[0]['x'])/2.0,(nut[1]['y']+nut[0]['y'])/2.0),
+ Point((bridge[1]['x']+bridge[0]['x'])/2.0,(bridge[1]['y']+bridge[0]['y'])/2.0))
+ for s in strings:
+ if nut.distanceToPoint(s[0])>=threshold or bridge.distanceToPoint(s[1])>=threshold:
+ doPartials = False
+ break
+
+ denom = ((bridge[1]['y']-bridge[0]['y'])*(nut[1]['x']-nut[0]['x']))-((bridge[1]['x']-bridge[0]['x'])*(nut[1]['y']-nut[0]['y']))
+ if denom != 0:
+ parallelFrets = False
+
+ fretboard = []
+ tones = len(scale)-1
+ for i in range(len(strings)):
+ base = tuning[i]
+ frets = []
+ if doPartials:
+ frets.append(Segment(meta[i][0],meta[i+1][0]))
+ else:
+ frets.append(Segment(strings[i][0],strings[i][0]))
+ last = strings[i][0]
+
+ for j in range(numfrets):
+ step=((base+j-1)%(tones))+1
+ ratio=1.0-((scale[step][1]*scale[step-1][0])/(scale[step][0]*scale[step-1][1]))
+ x = last['x']+(ratio*(strings[i][1]['x']-last['x']))
+ y = last['y']+(ratio*(strings[i][1]['y']-last['y']))
+ current = Point(x,y)
+ temp = Segment(strings[i][0],current)
+ totalRatio = temp.length()/strings[i].length()
+
+ if doPartials:
+ #partials depending on outer strings (questionable)
+ if parallelFrets:
+ temp = nut.createParallel(current)
+ else:
+ temp = Segment(strings[0].pointAtLength(strings[0].length()*totalRatio),
+ strings[-1].pointAtLength(strings[-1].length()*totalRatio))
+ frets.append(Segment(intersectSegments(temp,meta[i]),intersectSegments(temp,meta[i+1])))
+ else:
+ frets.append(Segment(current,current))
+ last = current
+ fretboard.append(frets)
+ return fretboard
+
+def FindStringsSingleScale(numStrings,scaleLength,nutWidth,bridgeWidth,oNF,oBF,oNL,oBL):
+ strings = []
+ meta = []
+ nutHalf = nutWidth/2
+ bridgeHalf = bridgeWidth/2
+ nutCandidateCenter = (nutHalf) + oNL
+ bridgeCandidateCenter = (bridgeHalf) + oBL
+ if bridgeCandidateCenter >= nutCandidateCenter:
+ center = bridgeCandidateCenter
+ else:
+ center = nutCandidateCenter
+ nutStringSpacing = nutWidth/(numStrings-1)
+ bridgeStringSpacing = bridgeWidth/(numStrings-1)
+
+ for i in range(numStrings):
+ strings.append(Segment(Point(center+nutHalf-(i*nutStringSpacing),0),
+ Point(center+bridgeHalf-(i*bridgeStringSpacing),scaleLength)))
+
+ meta.append(Segment(Point(center+nutHalf+oNF,0),Point(center+bridgeHalf+oBF,scaleLength)))
+ for i in range(1,numStrings):
+ meta.append(Segment(
+ Point((strings[i-1][0]['x']+strings[i][0]['x'])/2.0,
+ (strings[i-1][0]['y']+strings[i][0]['y'])/2.0),
+ Point((strings[i-1][1]['x']+strings[i][1]['x'])/2.0,
+ (strings[i-1][1]['y']+strings[i][1]['y'])/2.0)))
+ meta.append(Segment(Point(center-(nutHalf+oNL),0),Point(center-(bridgeHalf+oBL),scaleLength)))
+
+ return strings, meta
+
+def FindStringsMultiScale(numStrings,scaleLengthF,scaleLengthL,nutWidth,bridgeWidth,perp,oNF,oBF,oNL,oBL):
+ strings = []
+ meta = []
+ nutHalf = nutWidth/2
+ bridgeHalf = bridgeWidth/2
+ nutCandidateCenter = (nutHalf)+oNL
+ bridgeCandidateCenter = (bridgeHalf)+oBL
+ if bridgeCandidateCenter >= nutCandidateCenter:
+ xcenter = bridgeCandidateCenter
+ else:
+ nutCandidateCenter
+
+ fbnxf = xcenter+nutHalf+oNF
+ fbbxf = xcenter+bridgeHalf+oBF
+ fbnxl = xcenter-(nutHalf+oNL)
+ fbbxl = xcenter-(bridgeHalf+oBL)
+
+ snxf = xcenter+nutHalf
+ sbxf = xcenter+bridgeHalf
+ snxl = xcenter-nutHalf
+ sbxl = xcenter-bridgeHalf
+
+ fdeltax = sbxf-snxf
+ ldeltax = sbxl-snxl
+ fdeltay = math.sqrt((scaleLengthF*scaleLengthF)-(fdeltax*fdeltax))
+ ldeltay = math.sqrt((scaleLengthL*scaleLengthL)-(ldeltax*ldeltax))
+
+ fperp = perp*fdeltay
+ lperp = perp*ldeltay
+
+ #temporarily place first and last strings
+ first = Segment(Point(snxf,0),Point(sbxf,fdeltay))
+ last = Segment(Point(snxl,0),Point(sbxl,ldeltay))
+
+ if fdeltay<=ldeltay:
+ first.translate(0,(lperp-fperp))
+ else:
+ last.translate(0,(fperp-lperp))
+
+ nut = Segment(first[0].copy(),last[0].copy())
+ bridge = Segment(first[1].copy(),last[1].copy())
+ #overhang measurements are now converted from delta x to along line lengths
+ oNF = (oNF*nut.length())/nutWidth
+ oNL = (oNL*nut.length())/nutWidth
+ oBF = (oBF*bridge.length())/bridgeWidth
+ oBL = (oBL*bridge.length())/bridgeWidth
+ #place fretboard edges
+ fbf = Segment(nut.pointAtLength(-oNF),bridge.pointAtLength(-oBF))
+ fbl = Segment(nut.pointAtLength(nut.length()+oNL),bridge.pointAtLength(bridge.length()+oBL))
+ #normalize values into the first quadrant via translate
+ if fbf[0]['y']<0 or fbl[0]['y']<0:
+ if fbf[0]['y']<=fbl[0]['y']:
+ move = -fbf[0]['y']
+ else:
+ move = -fbl[0]['y']
+
+ first.translate(0,move)
+ last.translate(0,move)
+ nut.translate(0,move)
+ bridge.translate(0,move)
+ fbf.translate(0,move)
+ fbl.translate(0,move)
+
+ #output values
+ nutStringSpacing = nut.length()/(numStrings-1)
+ bridgeStringSpacing = bridge.length()/(numStrings-1)
+ strings.append(first)
+ for i in range(1,numStrings-1):
+ n = nut.pointAtLength(i*nutStringSpacing)
+ b = bridge.pointAtLength(i*bridgeStringSpacing)
+ strings.append(Segment(Point(n['x'],n['y']),Point(b['x'],b['y'])))
+ strings.append(last)
+
+ meta.append(fbf)
+ for i in range(1,numStrings):
+ meta.append(Segment(
+ Point((strings[i-1][0]['x']+strings[i][0]['x'])/2.0,
+ (strings[i-1][0]['y']+strings[i][0]['y'])/2.0),
+ Point((strings[i-1][1]['x']+strings[i][1]['x'])/2.0,
+ (strings[i-1][1]['y']+strings[i][1]['y'])/2.0)))
+
+ meta.append(fbl)
+
+ return strings, meta
diff --git a/share/extensions/ffscale.py b/share/extensions/ffscale.py
new file mode 100755
index 000000000..f9afa0b02
--- /dev/null
+++ b/share/extensions/ffscale.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+'''
+ Copyright (C) 2004 Aaron Cyril Spike
+
+ This file is part of FretFind 2-D.
+
+ FretFind 2-D is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ FretFind 2-D is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with FretFind 2-D; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math
+
+def ETScale(tones, octave=2.0):
+ octave, tones = float(octave), float(tones)
+ scale = {'steps':[[1.0,1.0]], 'title':'', 'errors':0, 'errorstring':''}
+ if not tones:
+ scale['errors'] += 1
+ scale['errorstring'] = 'Error: Number of tones must be non zero!'
+ else:
+ ratio = octave**(1/tones)
+ scale['title'] = '%s root of %s Equal Temperament' % (tones, octave)
+ scale['steps'].append([ratio,1])
+ return scale
+
+def ScalaScale(scala):
+ #initial step 0 or 1/1 is implicit
+ scale = {'steps':[[1.0,1.0]], 'title':'', 'errors':0, 'errorstring':''}
+
+ #split scale discarding commments
+ lines = [l.strip() for l in scala.strip().splitlines() if not l.strip().startswith('!')]
+
+ #first line may be blank and contains the title
+ scale['title'] = lines.pop(0)
+
+ #second line indicates the number of note lines that should follow
+ expected = int(lines.pop(0))
+
+ #discard blank lines and anything following whitespace
+ lines = [l.split()[0] for l in lines if l != '']
+
+ if len(lines) != expected:
+ scale['errors'] += 1
+ scale['errorstring'] = 'Error: expected %s more tones but found %s!' % (expected,len(lines))
+ else:
+ for l in lines:
+ #interpret anyline containing a dot as cents
+ if l.find('.') >= 0:
+ num = 2**(float(l)/1200)
+ denom = 1
+ #everything else is a ratio
+ elif l.find('/') >=0:
+ l = l.split('/')
+ num = float(int(l[0]))
+ denom = float(int(l[1]))
+ else:
+ num = float(int(l))
+ denom = 1.0
+ scale['steps'].append([num,denom])
+
+ if (num < 0) ^ (denom <= 0):
+ scale['errors'] += 1
+ scale['errorstring'] += 'Error at "'+l+'": Negative and undefined ratios are not allowed!\n'
+ return scale
diff --git a/share/extensions/ffset.inx b/share/extensions/ffset.inx
new file mode 100644
index 000000000..bba7ce558
--- /dev/null
+++ b/share/extensions/ffset.inx
@@ -0,0 +1,21 @@
+<inkscape-extension>
+ <_name>FretFind Single Length ET</_name>
+ <id>org.ekips.filter.fretfind.single.et</id>
+ <dependency type="executable" location="extensions">fretfind.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="scalelength" type="float" min="1" max="500" _gui-text="Scale Length">25</param>
+ <param name="nutwidth" type="float" min="0" max="100" _gui-text="Nut Width">2</param>
+ <param name="bridgewidth" type="float" min="0" max="100" _gui-text="Bridge Width">2.5</param>
+ <param name="etbase" type="float" min="1" max="10" _gui-text="Scale Base (2 for Octave)">2</param>
+ <param name="etroot" type="float" min="0.01" max="1000" _gui-text="Tones in Scale">12</param>
+ <param name="frets" type="int" min="1" max="1000" _gui-text="Number of Frets">24</param>
+ <param name="strings" type="int" min="1" max="60" _gui-text="Number of Strings">6</param>
+ <param name="fbedges" type="float" min="0" max="100" _gui-text="Fretboard Edges">0.0975</param>
+ <param name="pxperunit" type="float" min="0" max="1000" _gui-text="px per Unit">90</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">fretfind.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/ffss.inx b/share/extensions/ffss.inx
new file mode 100644
index 000000000..102b50285
--- /dev/null
+++ b/share/extensions/ffss.inx
@@ -0,0 +1,21 @@
+<inkscape-extension>
+ <_name>FretFind Single Length Scala</_name>
+ <id>org.ekips.filter.fretfind.single.scala</id>
+ <dependency type="executable" location="extensions">fretfind.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="scalelength" type="float" min="1" max="500" _gui-text="Scale Length">25</param>
+ <param name="nutwidth" type="float" min="0" max="100" _gui-text="Nut Width">2</param>
+ <param name="bridgewidth" type="float" min="0" max="100" _gui-text="Bridge Width">2.5</param>
+ <param name="scalafile" type="string" _gui-text="Path to Scala *.scl File">none</param>
+ <param name="tuning" type="string" _gui-text="Tuning (Scale step for each string seperated by semicolons)">0;0;0;0;0;0</param>
+ <param name="frets" type="int" min="1" max="1000" _gui-text="Number of Frets">24</param>
+ <param name="strings" type="int" min="1" max="60" _gui-text="Number of Strings">6</param>
+ <param name="fbedges" type="float" min="0" max="100" _gui-text="Fretboard Edges">0.0975</param>
+ <param name="pxperunit" type="float" min="0" max="1000" _gui-text="px per Unit">90</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">fretfind.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/fretfind.py b/share/extensions/fretfind.py
new file mode 100755
index 000000000..310a09a5d
--- /dev/null
+++ b/share/extensions/fretfind.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import sys
+import inkex, simplestyle, simplepath
+import ffscale, ffproc
+
+def seg2path(seg):
+ return "M%s,%sL%s,%s" % (seg[0]['x'],seg[0]['y'],seg[1]['x'],seg[1]['y'])
+
+class FretFind(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.doScala = False
+ self.doMultiScale = False
+ self.OptionParser.add_option("--scalafile",
+ action="store", type="string",
+ dest="scalafile", default=None,
+ help="")
+ self.OptionParser.add_option("--scalascale",
+ action="store", type="string",
+ dest="scalascale", default=None,
+ help="")
+ self.OptionParser.add_option("--etbase",
+ action="store", type="float",
+ dest="etbase", default=2.0,
+ help="")
+ self.OptionParser.add_option("--etroot",
+ action="store", type="float",
+ dest="etroot", default=12.0,
+ help="")
+ self.OptionParser.add_option("--tuning",
+ action="store", type="string",
+ dest="tuning", default="0;0;0;0;0;0",
+ help="")
+ self.OptionParser.add_option("--perpdist",
+ action="store", type="float",
+ dest="perpdist", default=0.5,
+ help="")
+ self.OptionParser.add_option("--offset",
+ action="store", type="float",
+ dest="offset", default=0.0,
+ help="")
+ self.OptionParser.add_option("--scalelength",
+ action="store", type="float",
+ dest="scalelength", default=25.0,
+ help="")
+ self.OptionParser.add_option("--firstscalelength",
+ action="store", type="float",
+ dest="firstscalelength", default=None,
+ help="")
+ self.OptionParser.add_option("--lastscalelength",
+ action="store", type="float",
+ dest="lastscalelength", default=None,
+ help="")
+ self.OptionParser.add_option("--nutwidth",
+ action="store", type="float",
+ dest="nutwidth", default=1.5,
+ help="")
+ self.OptionParser.add_option("--bridgewidth",
+ action="store", type="float",
+ dest="bridgewidth", default=2.5,
+ help="")
+ self.OptionParser.add_option("--frets",
+ action="store", type="int",
+ dest="frets", default=24,
+ help="")
+ self.OptionParser.add_option("--strings",
+ action="store", type="int",
+ dest="strings", default=6,
+ help="")
+ self.OptionParser.add_option("--fbedges",
+ action="store", type="float",
+ dest="fbedges", default=0.0975,
+ help="")
+ self.OptionParser.add_option("--pxperunit",
+ action="store", type="float",
+ dest="pxperunit", default=90,
+ help="")
+ def checkopts(self):
+ #scale type
+ if self.options.scalascale is not None:
+ self.doScala = True
+ if self.options.scalafile is not None:
+ if self.doScala:
+ sys.stderr.write('Mutually exclusive options: if found scalafile will override scalascale.')
+ else:
+ self.options.scalascale = ""
+ self.doScala = True
+ try:
+ f = open(self.options.scalafile,'r')
+ self.options.scalascale = f.read()
+ except:
+ sys.exit('Scala scale description file expected.')
+ #scale
+ if self.doScala:
+ self.scale = ffscale.ScalaScale(self.options.scalascale)
+ else:
+ self.scale = ffscale.ETScale(self.options.etroot,self.options.etbase)
+
+ #string length
+ first = self.options.firstscalelength is not None
+ last = self.options.lastscalelength is not None
+ if first and last:
+ self.doMultiScale = True
+ elif first:
+ sys.stderr.write('Missing lastscalelength: overriding scalelength with firstscalelength.')
+ self.options.scalelength = self.options.firstscalelength
+ elif last:
+ sys.stderr.write('Missing firstscalelength: overriding scalelength with lastscalelength.')
+ self.options.scalelength = self.options.lastscalelength
+
+ #tuning
+ self.tuning = [int(t.strip()) for t in self.options.tuning.split(";")]
+ self.tuning.extend([0 for i in range(self.options.strings - len(self.tuning))])
+
+ def effect(self):
+ self.checkopts()
+
+ o = self.options.fbedges
+ oNF,oBF,oNL,oBL = o,o,o,o
+
+ if self.doMultiScale:
+ strings, meta = ffproc.FindStringsMultiScale(self.options.strings,self.options.firstscalelength,
+ self.options.lastscalelength,self.options.nutwidth,self.options.bridgewidth,
+ self.options.perpdist,oNF,oBF,oNL,oBL)
+ else:
+ strings, meta = ffproc.FindStringsSingleScale(self.options.strings,self.options.scalelength,
+ self.options.nutwidth,self.options.bridgewidth,
+ oNF,oBF,oNL,oBL)
+
+ frets = ffproc.FindFrets(strings, meta, self.scale, self.tuning, self.options.frets)
+
+ edgepath = seg2path(meta[0]) + seg2path(meta[-1])
+ stringpath = "".join([seg2path(s) for s in strings])
+ fretpath = "".join(["".join([seg2path(f) for f in s]) for s in frets])
+
+ group = self.document.createElement('svg:g')
+ group.setAttribute('transform',"scale(%s,%s)" % (self.options.pxperunit,self.options.pxperunit))
+ self.document.documentElement.appendChild(group)
+
+ edge = self.document.createElement('svg:path')
+ s = {'stroke-linejoin': 'miter', 'stroke-width': '0.01px',
+ 'stroke-opacity': '1.0', 'fill-opacity': '1.0',
+ 'stroke': '#0000FF', 'stroke-linecap': 'butt',
+ 'fill': 'none'}
+ edge.setAttribute('style', simplestyle.formatStyle(s))
+ edge.setAttribute('d', edgepath)
+
+ string = self.document.createElement('svg:path')
+ s = {'stroke-linejoin': 'miter', 'stroke-width': '0.01px',
+ 'stroke-opacity': '1.0', 'fill-opacity': '1.0',
+ 'stroke': '#FF0000', 'stroke-linecap': 'butt',
+ 'fill': 'none'}
+ string.setAttribute('style', simplestyle.formatStyle(s))
+ string.setAttribute('d', stringpath)
+
+ fret = self.document.createElement('svg:path')
+ s = {'stroke-linejoin': 'miter', 'stroke-width': '0.01px',
+ 'stroke-opacity': '1.0', 'fill-opacity': '1.0',
+ 'stroke': '#000000', 'stroke-linecap': 'butt',
+ 'fill': 'none'}
+ fret.setAttribute('style', simplestyle.formatStyle(s))
+ fret.setAttribute('d', fretpath)
+
+ group.appendChild(edge)
+ group.appendChild(string)
+ group.appendChild(fret)
+
+e = FretFind()
+e.affect()
diff --git a/share/extensions/gimpgrad.inx b/share/extensions/gimpgrad.inx
new file mode 100644
index 000000000..7441694dc
--- /dev/null
+++ b/share/extensions/gimpgrad.inx
@@ -0,0 +1,14 @@
+<inkscape-extension>
+ <_name>GIMP Gradients</_name>
+ <id>org.inkscape.input.gimpgrad</id>
+ <dependency type="plugin" location="plugins">gimpgrad</dependency>
+ <input>
+ <extension>.ggr</extension>
+ <mimetype>application/x-gimp-gradient</mimetype>
+ <_filetypename>GIMP Gradient (*.ggr)</_filetypename>
+ <_filetypetooltip>Gradients used in GIMP</_filetypetooltip>
+ </input>
+ <plugin>
+ <name>gimpgrad</name>
+ </plugin>
+</inkscape-extension>
diff --git a/share/extensions/grid.inx b/share/extensions/grid.inx
new file mode 100644
index 000000000..aaaef71af
--- /dev/null
+++ b/share/extensions/grid.inx
@@ -0,0 +1,16 @@
+<inkscape-extension>
+ <_name>Grid</_name>
+ <id>org.inkscape.effect.grid</id>
+ <dependency type="plugin" location="plugins">grid</dependency>
+ <param name="lineWidth" type="float">1.0</param>
+ <param name="xspacing" type="float">10.0</param>
+ <param name="yspacing" type="float">10.0</param>
+ <param name="xoffset" type="float">5.0</param>
+ <param name="yoffset" type="float">5.0</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <plugin>
+ <name>grid</name>
+ </plugin>
+</inkscape-extension>
diff --git a/share/extensions/handles.inx b/share/extensions/handles.inx
new file mode 100644
index 000000000..d0c42aafc
--- /dev/null
+++ b/share/extensions/handles.inx
@@ -0,0 +1,12 @@
+<inkscape-extension>
+ <_name>Draw Handles</_name>
+ <id>org.ekips.filter.handles</id>
+ <dependency type="executable" location="extensions">handles.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">handles.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/handles.py b/share/extensions/handles.py
new file mode 100755
index 000000000..a77b84ccc
--- /dev/null
+++ b/share/extensions/handles.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import inkex, simplepath, simplestyle
+
+class Handles(inkex.Effect):
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ p = simplepath.parsePath(node.attributes.getNamedItem('d').value)
+ a =[]
+ pen = None
+ subPathStart = None
+ for cmd,params in p:
+ if cmd == 'C':
+ a.extend([['M', params[:2]], ['L', pen],
+ ['M', params[2:4]], ['L', params[-2:]]])
+ if cmd == 'Q':
+ a.extend([['M', params[:2]], ['L', pen],
+ ['M', params[:2]], ['L', params[-2:]]])
+
+ if cmd == 'M':
+ subPathStart = params
+
+ if cmd == 'Z':
+ pen = subPathStart
+ else:
+ pen = params[-2:]
+
+ if len(a) > 0:
+ new = self.document.createElement('svg:path')
+ s = {'stroke-linejoin': 'miter', 'stroke-width': '1.0px',
+ 'stroke-opacity': '1.0', 'fill-opacity': '1.0',
+ 'stroke': '#000000', 'stroke-linecap': 'butt',
+ 'fill': 'none'}
+ new.setAttribute('style', simplestyle.formatStyle(s))
+ new.setAttribute('d', simplepath.formatPath(a))
+ node.parentNode.appendChild(new)
+
+e = Handles()
+e.affect()
diff --git a/share/extensions/ill2svg.pl b/share/extensions/ill2svg.pl
new file mode 100644
index 000000000..876888690
--- /dev/null
+++ b/share/extensions/ill2svg.pl
@@ -0,0 +1,374 @@
+#!/usr/bin/perl
+# convert an illustrator file (on stdin) to svg (on stdout)
+use strict;
+use warnings;
+use Getopt::Std;
+
+my $skip_images;
+
+BEGIN {
+ $skip_images = 0;
+ eval "use Image::Magick;";
+ if ($@) {
+ warn "Couldn't load Perl module Image::Magick. Images will be skipped.\n";
+ warn "$@\n";
+ $skip_images = 1;
+ }
+}
+
+
+my %args;
+
+# Newline characters
+my $NL_DOS = "\015\012";
+my $NL_MAC = "\015";
+my $NL_UNX = "\012";
+
+getopts('h:', \%args);
+
+my @ImageData;
+my $pagesize=1052.36218;
+my $imagewidth = 128;
+my $imageheight = 88;
+my $imagex = 0;
+my $imagey = 0;
+my $imagenum = 0;
+my $strokeparams;
+my $strokecolor;
+my $strokewidth;
+my $fillcolor;
+my $firstChar = 0;
+my $image;
+my $path;
+my $color = 0;
+my $weareinimage=0;
+my $weareintext=0;
+my($red,$green,$blue);
+my($cpx,$cpy);
+
+if ($args{h}) { usage() && exit }
+
+$color = "#000";
+
+sub addImageLine {
+ my ($data) = @_;
+ chomp($data);
+ #push (@ImageData, $data);
+
+ my $len = length( $data );
+ my $count = 0;
+
+ #printf("%s %d\r\n",$data,$len);
+ #for( $loop=1; $loop < ($len - 2); $loop += 2){
+ for( my $loop=1; $loop < $len; $loop += 2){
+ my $value = substr( $data, $loop, 2);
+
+ #printf("[%d:%s]",$loop,$value);
+
+ if( $color == 0 ){
+ #$red = hex($value);
+ $red = $value;
+ $color ++;
+ #printf("Color: RED: %s,",$red);
+ } elsif ( $color == 1 ) {
+ #$green = hex($value);
+ $green = $value;
+ $color ++;
+ #printf("Color: GREEN %s,",$green);
+
+ } else {
+ $blue = $value;
+ my $pixel="pixel[${imagex},${imagey}]";
+ my $rgb=sprintf("#%s%s%s",$red,$green,$blue);
+ #$image->Set($pixel=>$rgb);
+ $image->Set("pixel[${imagex},${imagey}]"=>"#${red}${green}${blue}")
+ unless ($skip_images);
+ #printf("PIXX: %d ",$image->Get($pixel));
+ $color = 0;
+ $imagex++;
+
+ #printf("Color:BLUE: %s, X: %d Y: %d %dx%d %s [%s]\r\n",$blue,$imagex,$imagey,$imagewidth,$imageheight,$rgb,$pixel);
+
+ if( $imagex == $imagewidth ){
+ $imagex = 0;
+ $imagey ++;
+ } # if
+
+
+ } #else
+
+ }
+
+ #printf("len: %d %dx%d\r\n",$len,$imagewidth,$imageheight);
+
+}
+
+sub cmyk_to_css {
+ my ($c, $m, $y, $k) = @_;
+ my ($r, $g, $b);
+
+ $r = 1 - ($k + $c);
+ if ($r < 0) { $r = 0; }
+ $g = 1 - ($k + $m);
+ if ($g < 0) { $g = 0; }
+ $b = 1 - ($k + $y);
+ if ($b < 0) { $b = 0; }
+ return sprintf ("#%02x%02x%02x", 255 * $r, 255 * $g, 255 * $b);
+}
+
+sub nice_float {
+ my ($x) = @_;
+
+ my $result = sprintf ("%.3f", $x);
+ $result =~ s/0*$//;
+ $result =~ s/\.$//;
+ return $result;
+}
+
+sub xform_xy {
+ my ($x, $y) = @_;
+ my @result = ();
+
+ for my $i (0..$#_) {
+ if ($i & 1) {
+ #push @result, 1000 - $_[$i];
+ push @result, $pagesize - $_[$i];
+ } else {
+ #push @result, $_[$i] - 100;
+ push @result, $_[$i];
+ }
+ }
+ return join ' ', map { nice_float ($_) } @result;
+}
+
+sub strokeparams {
+ $strokecolor ||= 'black';
+ my $result = "stroke:$strokecolor";
+ if ($strokewidth && $strokewidth != 1) {
+ $result .= "; stroke-width:$strokewidth";
+ }
+ return $result;
+}
+
+sub usage {
+ warn qq|Usage: ill2svg [-l "string" -h] infile > outfile
+options:
+ -h print this message and exit
+|;
+}
+
+sub process_line {
+ chomp;
+ return if /^%_/;
+
+ if (/^([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) k$/) {
+ $fillcolor = cmyk_to_css ($1, $2, $3, $4);
+ } elsif (/^([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) K$/) {
+ $strokecolor = cmyk_to_css ($1, $2, $3, $4);
+ } elsif (/^([\d\.]+) g$/) {
+ $fillcolor = cmyk_to_css (0, 0, 0, 1 - $1);
+ } elsif (/^([\d\.]+) G$/) {
+ $strokecolor = cmyk_to_css (0, 0, 0, 1 - $1);
+ } elsif (/^([\d\.]+) ([\d\.]+) m$/) {
+ $path .= 'M'.xform_xy($1, $2);
+ $cpx = $1;
+ $cpy = $2;
+ } elsif (/^([\d\.]+) ([\d\.]+) l$/i) {
+ $path .= 'L'.xform_xy($1, $2);
+ $cpx = $1;
+ $cpy = $2;
+ } elsif (/^([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) v$/i) {
+ $path .= 'C'.xform_xy($cpx, $cpy, $1, $2, $3, $4);
+ $cpx = $3;
+ $cpy = $4;
+ } elsif (/^([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) y$/i) {
+ $path .= 'C'.xform_xy($1, $2, $3, $4, $3, $4);
+ $cpx = $3;
+ $cpy = $4;
+ } elsif (/^([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) c$/i) {
+ $path .= 'C'.xform_xy($1, $2, $3, $4, $5, $6);
+ $cpx = $5;
+ $cpy = $6;
+ } elsif (/^b$/) {
+ $path .= 'z';
+ $strokeparams = strokeparams ();
+ print " <g style=\"fill: $fillcolor; $strokeparams\">\n";
+ print " <path d=\"$path\"/>\n";
+ print " </g>\n";
+ $path = '';
+ } elsif (/^B$/) {
+ $strokeparams = strokeparams ();
+ print " <g style=\"fill: $fillcolor; $strokeparams\">\n";
+ print " <path d=\"$path\"/>\n";
+ print " </g>\n";
+ $path = '';
+ } elsif (/^f$/i) {
+ $path .= 'z';
+ if (! $fillcolor) {
+ warn "Error: Fill color not defined in source file!\n";
+ print " <path d=\"$path\"/>\n";
+ } else {
+ print " <g style=\"fill: $fillcolor;\">\n";
+ print " <path d=\"$path\"/>\n";
+ print " </g>\n";
+ }
+ $path = '';
+ } elsif (/^s$/) {
+ $path .= 'z';
+ $strokeparams = strokeparams ();
+ #print " <g style=\"fill:none;stroke:black;stroke-opacity:1; $strokeparams\">\n";
+ print " <g style=\"fill:none; $strokeparams\">\n";
+ print " <path d=\"$path\"/>\n";
+ print " </g>\n";
+ $path = '';
+ } elsif (/^S$/) {
+ $strokeparams = strokeparams ();
+ #print " <g style=\"fill:none; $strokeparams\">\n";
+ print " <g style=\"fill:none;stroke:black;$strokeparams;\">\n";
+ print " <path d=\"$path\"/>\n";
+ print " </g>\n";
+ $path = '';
+ } elsif (/^1 XR$/) {
+
+ if( $firstChar != 0){
+ print (" </tspan>\n</text>\n");
+ }
+
+ $weareintext=0;
+
+ $firstChar = 0;
+ } elsif (/^TP$/) {
+ #Something do with the text;)
+# $weareintext=1;
+ } elsif (/^([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) Tp$/i) {
+
+ # Text position etc;
+ $cpx=$5;
+ $cpy=$pagesize - $6;
+ ## print ("x:$5 y:$6\r\n");
+
+ } elsif (/^TO$/) {
+ #one text ends
+ } elsif (/^LB$/) {
+ #Everything ends??
+ ## Sometimes ain't working
+ warn "Error: Unexpected 'LB'! Everything has ended??\n";
+ if( $weareintext != 0){
+ print (" </tspan>\n</text>\n");
+ }
+
+ } elsif (/^\/_([\S\s]+) ([\d\.]+) Tf$/) {
+ my $FontName = $1;
+ my $FontSize = $2;
+
+ ## When we know font name we can render this.
+ if( $firstChar != 1){
+ print ("<text x=\"$cpx\" y=\"$cpy\"");
+ print (" style=\"font-size:$FontSize;font-weight:normal;stroke-width:1;");
+ print ("fill:$fillcolor;fill-opacity:1;");
+ print ("font-family:$FontName;\" id=\"text$5\">\n<tspan>");
+ $firstChar = 1;
+ $weareintext=1;
+ } else {
+ print ("</tspan>\n<tspan x=\"$cpx\" y=\"$cpy\"");
+ print (" style=\"font-size:$FontSize;font-weight:normal;stroke-width:1;");
+ print ("fill:$fillcolor;fill-opacity:1;");
+ print ("font-family:$FontName;\" id=\"text$5\">");
+ $weareintext=1;
+ }
+
+ } elsif (/^\(([\S\s]+)\) Tx$/) {
+ # Normal text
+ my $text = $1;
+ $text =~ s/ä/ä/;
+ $text =~ s/ö/ö/;
+ $text =~ s/å/Ã¥/;
+
+ print ("$text");
+
+ } elsif (/([\d\.]+) w$/) {
+ $strokewidth = $1;
+ } elsif (/^%%BeginData: ([\d\.]+)/) {
+ #How much we got image data
+ } elsif(/^%%EndData/) {
+ #and the data ends
+ } elsif (/^XI/) {
+ #actual image starts
+ $weareinimage=1;
+ } elsif (/^XH/) {
+ #it ends like they allways do.
+ #we just save it after this..
+ $weareinimage=0;
+ my $imagename="${imagenum}.png";
+ $image->Write($imagename) unless ($skip_images);
+ $imagenum++;
+ } elsif (/\[(.*)\](.*)Xh/) {
+ my @imagepos = split(/ /, $1);
+ my @imageinfo = split(/ /, $2);
+ $imagewidth = $imageinfo[1];
+ $imageheight = $imageinfo[2];
+
+ if (! $imagewidth && ! $imageheight ) {
+ die "ERROR: This fileformat is not supported by "
+ ."ill2svg.pl.\n(Is it possible you are trying to "
+ ."use ill2svg.pl on a PDF file?) \n";
+ }
+
+ my $imageposx = $imagepos[4];
+ my $imageposy = $pagesize - $imagepos[5];
+
+ #printf("%s %d %d Position x: %f 9y: %f\r\n",$1,@imagepos[4],@imagepos[5],$imageposx,$imageposy);
+ printf("<image");
+ printf(" xlink:href=\"%d.png\"",$imagenum);
+ printf(" width=\"%d\"",$imagewidth);
+ printf(" height=\"%d\"",$imageheight);
+ printf(" x=\"%f\"",$imageposx);
+ printf(" y=\"%f\"",$imageposy);
+ printf("/>\r\n");
+
+ my $size = "${imagewidth}x${imageheight}";
+
+ if (!$skip_images) {
+ $image = Image::Magick->new(size=>$size);
+ $image->Set('density'=>'72');
+ $image->Read("xc:white");
+ }
+ $imagex=0;
+ $imagey=0;
+
+ } else {
+ if( $weareinimage == 1 ){
+ addImageLine( $_ );
+ }
+
+ chomp;
+
+ #print "$_\r\n";
+ }
+}
+ if( $firstChar != 0){
+ print (" </tspan>\n</text>\n");
+ }
+
+print "<svg";
+print " xmlns=\"http://www.w3.org/2000/svg\"";
+print " xmlns:xlink=\"http://www.w3.org/1999/xlink\">\r\n";
+
+while (<>) {
+ if (m/$NL_DOS$/) {
+ $/ = $NL_DOS;
+ foreach (split /$NL_DOS/) {
+ process_line($_);
+ }
+ } elsif (m/$NL_MAC$/) {
+ $/ = $NL_MAC;
+ foreach (split /$NL_MAC/) {
+ process_line($_);
+ }
+ } else {
+ chomp;
+ process_line($_);
+ }
+}
+print "</svg>\n";
+
diff --git a/share/extensions/inkex.py b/share/extensions/inkex.py
new file mode 100755
index 000000000..041f7bff7
--- /dev/null
+++ b/share/extensions/inkex.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+"""
+inkex.py
+A helper module for creating Inkscape extensions
+
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+import sys, copy, optparse
+
+#a dictionary of all of the xmlns prefixes in a standard inkscape doc
+NSS = {
+u'sodipodi' :u'http://inkscape.sourceforge.net/DTD/sodipodi-0.dtd',
+u'cc' :u'http://web.resource.org/cc/',
+u'svg' :u'http://www.w3.org/2000/svg',
+u'dc' :u'http://purl.org/dc/elements/1.1/',
+u'rdf' :u'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+u'inkscape' :u'http://www.inkscape.org/namespaces/inkscape',
+u'xlink' :u'http://www.w3.org/1999/xlink'
+}
+
+try:
+ import xml.dom.ext
+ import xml.dom.ext.reader.Sax2
+ import xml.xpath
+except:
+ sys.exit('The inkex.py module requires PyXML. Please download the latest version from <http://pyxml.sourceforge.net/>.')
+
+def debug(what):
+ sys.stderr.write(str(what) + "\n")
+ return what
+
+def check_inkbool(option, opt, value):
+ if str(value).capitalize() == 'True':
+ return True
+ elif str(value).capitalize() == 'False':
+ return False
+ else:
+ raise OptionValueError("option %s: invalid inkbool value: %s" % (opt, value))
+
+class InkOption(optparse.Option):
+ TYPES = optparse.Option.TYPES + ("inkbool",)
+ TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)
+ TYPE_CHECKER["inkbool"] = check_inkbool
+
+
+class Effect:
+ """A class for creating Inkscape SVG Effects"""
+ def __init__(self):
+ self.document=None
+ self.selected={}
+ self.options=None
+ self.args=None
+ self.OptionParser = optparse.OptionParser(usage="usage: %prog [options] SVGfile",option_class=InkOption)
+ self.OptionParser.add_option("--id",
+ action="append", type="string", dest="ids", default=[],
+ help="id attribute of object to manipulate")
+ def effect(self):
+ pass
+ def getoptions(self,args=sys.argv[1:]):
+ """Collect command line arguments"""
+ self.options, self.args = self.OptionParser.parse_args(args)
+ def parse(self,file=None):
+ """Parse document in specified file or on stdin"""
+ reader = xml.dom.ext.reader.Sax2.Reader()
+ try:
+ try:
+ stream = open(file,'r')
+ except:
+ stream = open(self.args[-1],'r')
+ except:
+ stream = sys.stdin
+ self.document = reader.fromStream(stream)
+ stream.close()
+ def getselected(self):
+ """Collect selected nodes"""
+ for id in self.options.ids:
+ path = '//*[@id="%s"]' % id
+ for node in xml.xpath.Evaluate(path,self.document):
+ self.selected[id] = node
+ def output(self):
+ """Serialize document into XML on stdout"""
+ xml.dom.ext.Print(self.document)
+ def affect(self):
+ """Affect an SVG document with a callback effect"""
+ self.getoptions()
+ self.parse()
+ self.getselected()
+ self.effect()
+ self.output()
diff --git a/share/extensions/inkscape-shadow-white.sh b/share/extensions/inkscape-shadow-white.sh
new file mode 100755
index 000000000..c1cb0268c
--- /dev/null
+++ b/share/extensions/inkscape-shadow-white.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+convert -mattecolor "#ffff" -frame $((${2} * 3))x$((${2} * 3)) -fx '1' -channel A -blur $((${2} * 3))x${2} $1 $1
diff --git a/share/extensions/inkscape-shadow.sh b/share/extensions/inkscape-shadow.sh
new file mode 100755
index 000000000..7a3e45fa9
--- /dev/null
+++ b/share/extensions/inkscape-shadow.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+convert -mattecolor "#000f" -frame $((${2} * 3))x$((${2} * 3)) -fx '0' -channel A -blur $((${2} * 3))x${2} $1 $1
diff --git a/share/extensions/interp.inx b/share/extensions/interp.inx
new file mode 100644
index 000000000..edc1ff4da
--- /dev/null
+++ b/share/extensions/interp.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>Interpolate</_name>
+ <id>org.ekips.filter.interp</id>
+ <dependency type="executable" location="extensions">interp.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="exponent" type="float" min="-100.0" max="100.0" _gui-text="Exponent">0.00</param>
+ <param name="steps" type="int" min="1" max="1000" _gui-text="Interpolation Steps">5</param>
+ <param name="method" type="int" min="1" max="2" _gui-text="Interpolation Method">2</param>
+ <param name="dup" type="boolean" _gui-text="Duplicate Endpaths">true</param>
+ <param name="style" type="boolean" _gui-text="Interpolate Style (Experimental)">false</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">interp.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/interp.py b/share/extensions/interp.py
new file mode 100755
index 000000000..5edbf026d
--- /dev/null
+++ b/share/extensions/interp.py
@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import inkex, cubicsuperpath, simplestyle, copy, math, re, bezmisc
+
+uuconv = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'pc':15.0}
+def numsegs(csp):
+ return sum([len(p)-1 for p in csp])
+def interpcoord(v1,v2,p):
+ return v1+((v2-v1)*p)
+def interppoints(p1,p2,p):
+ return [interpcoord(p1[0],p2[0],p),interpcoord(p1[1],p2[1],p)]
+def pointdistance((x1,y1),(x2,y2)):
+ return math.sqrt(((x2 - x1) ** 2) + ((y2 - y1) ** 2))
+def bezlenapprx(sp1, sp2):
+ return pointdistance(sp1[1], sp1[2]) + pointdistance(sp1[2], sp2[0]) + pointdistance(sp2[0], sp2[1])
+def tpoint((x1,y1), (x2,y2), t = 0.5):
+ return [x1+t*(x2-x1),y1+t*(y2-y1)]
+def cspbezsplit(sp1, sp2, t = 0.5):
+ m1=tpoint(sp1[1],sp1[2],t)
+ m2=tpoint(sp1[2],sp2[0],t)
+ m3=tpoint(sp2[0],sp2[1],t)
+ m4=tpoint(m1,m2,t)
+ m5=tpoint(m2,m3,t)
+ m=tpoint(m4,m5,t)
+ return [[sp1[0][:],sp1[1][:],m1], [m4,m,m5], [m3,sp2[1][:],sp2[2][:]]]
+def cspbezsplitatlength(sp1, sp2, l = 0.5, tolerance = 0.001):
+ bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+ t = bezmisc.beziertatlength(bez, l, tolerance)
+ return cspbezsplit(sp1, sp2, t)
+def cspseglength(sp1,sp2, tolerance = 0.001):
+ bez = (sp1[1][:],sp1[2][:],sp2[0][:],sp2[1][:])
+ return bezmisc.bezierlength(bez, tolerance)
+def csplength(csp):
+ total = 0
+ lengths = []
+ for sp in csp:
+ lengths.append([])
+ for i in xrange(1,len(sp)):
+ l = cspseglength(sp[i-1],sp[i])
+ lengths[-1].append(l)
+ total += l
+ return lengths, total
+def styleunittouu(string):
+ unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
+ param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
+
+ p = param.match(string)
+ u = unit.search(string)
+ if p:
+ retval = float(p.string[p.start():p.end()])
+ else:
+ retval = 0.0
+ if u:
+ try:
+ return retval * uuconv[u.string[u.start():u.end()]]
+ except KeyError:
+ pass
+ return retval
+
+def tweenstylefloat(property, start, end, time):
+ sp = float(start[property])
+ ep = float(end[property])
+ return str(sp + (time * (ep - sp)))
+def tweenstyleunit(property, start, end, time):
+ sp = styleunittouu(start[property])
+ ep = styleunittouu(end[property])
+ return str(sp + (time * (ep - sp)))
+def tweenstylecolor(property, start, end, time):
+ sr,sg,sb = parsecolor(start[property])
+ er,eg,eb = parsecolor(end[property])
+ return '#%s%s%s' % (tweenhex(time,sr,er),tweenhex(time,sg,eg),tweenhex(time,sb,eb))
+def tweenhex(time,s,e):
+ s = float(int(s,16))
+ e = float(int(e,16))
+ retval = hex(int(math.floor(s + (time * (e - s)))))[2:]
+ if len(retval)==1:
+ retval = '0%s' % retval
+ return retval
+def parsecolor(c):
+ r,g,b = '0','0','0'
+ if c[:1]=='#':
+ if len(c)==4:
+ r,g,b = c[1:2],c[2:3],c[3:4]
+ elif len(c)==7:
+ r,g,b = c[1:3],c[3:5],c[5:7]
+ return r,g,b
+
+class Interp(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-e", "--exponent",
+ action="store", type="float",
+ dest="exponent", default=0.0,
+ help="values other than zero give non linear interpolation")
+ self.OptionParser.add_option("-s", "--steps",
+ action="store", type="int",
+ dest="steps", default=5,
+ help="number of interpolation steps")
+ self.OptionParser.add_option("-m", "--method",
+ action="store", type="int",
+ dest="method", default=2,
+ help="method of interpolation")
+ self.OptionParser.add_option("-d", "--dup",
+ action="store", type="inkbool",
+ dest="dup", default=True,
+ help="duplicate endpaths")
+ self.OptionParser.add_option("--style",
+ action="store", type="inkbool",
+ dest="style", default=True,
+ help="try interpolation of some style properties")
+ def effect(self):
+ exponent = self.options.exponent
+ if exponent>= 0:
+ exponent = 1.0 + exponent
+ else:
+ exponent = 1.0/(1.0 - exponent)
+ steps = [1.0/(self.options.steps + 1.0)]
+ for i in range(self.options.steps - 1):
+ steps.append(steps[0] + steps[-1])
+ steps = [step**exponent for step in steps]
+
+ paths = {}
+ styles = {}
+ for id in self.options.ids:
+ node = self.selected[id]
+ if node.tagName =='path':
+ paths[id] = cubicsuperpath.parsePath(node.attributes.getNamedItem('d').value)
+ styles[id] = simplestyle.parseStyle(node.attributes.getNamedItem('style').value)
+ else:
+ self.options.ids.remove(id)
+
+ for i in range(1,len(self.options.ids)):
+ start = copy.deepcopy(paths[self.options.ids[i-1]])
+ end = copy.deepcopy(paths[self.options.ids[i]])
+ sst = copy.deepcopy(styles[self.options.ids[i-1]])
+ est = copy.deepcopy(styles[self.options.ids[i]])
+ basestyle = copy.deepcopy(sst)
+
+ #prepare for experimental style tweening
+ if self.options.style:
+ dostroke = True
+ dofill = True
+ styledefaults = {'opacity':'1.0', 'stroke-opacity':'1.0', 'fill-opacity':'1.0',
+ 'stroke-width':'1.0', 'stroke':'none', 'fill':'none'}
+ for key in styledefaults.keys():
+ sst.setdefault(key,styledefaults[key])
+ est.setdefault(key,styledefaults[key])
+ isnotplain = lambda x: not (x=='none' or x[:1]=='#')
+ if isnotplain(sst['stroke']) or isnotplain(est['stroke']) or (sst['stroke']=='none' and est['stroke']=='none'):
+ dostroke = False
+ if isnotplain(sst['fill']) or isnotplain(est['fill']) or (sst['fill']=='none' and est['fill']=='none'):
+ dofill = False
+ if dostroke:
+ if sst['stroke']=='none':
+ sst['stroke-width'] = '0.0'
+ sst['stroke-opacity'] = '0.0'
+ sst['stroke'] = est['stroke']
+ elif est['stroke']=='none':
+ est['stroke-width'] = '0.0'
+ est['stroke-opacity'] = '0.0'
+ est['stroke'] = sst['stroke']
+ if dofill:
+ if sst['fill']=='none':
+ sst['fill-opacity'] = '0.0'
+ sst['fill'] = est['fill']
+ elif est['fill']=='none':
+ est['fill-opacity'] = '0.0'
+ est['fill'] = sst['fill']
+
+
+
+ if self.options.method == 2:
+ #subdivide both paths into segments of relatively equal lengths
+ slengths, stotal = csplength(start)
+ elengths, etotal = csplength(end)
+ lengths = {}
+ t = 0
+ for sp in slengths:
+ for l in sp:
+ t += l / stotal
+ lengths.setdefault(t,0)
+ lengths[t] += 1
+ t = 0
+ for sp in elengths:
+ for l in sp:
+ t += l / etotal
+ lengths.setdefault(t,0)
+ lengths[t] += -1
+ sadd = [k for (k,v) in lengths.iteritems() if v < 0]
+ sadd.sort()
+ eadd = [k for (k,v) in lengths.iteritems() if v > 0]
+ eadd.sort()
+
+ t = 0
+ s = [[]]
+ for sp in slengths:
+ if not start[0]:
+ s.append(start.pop(0))
+ s[-1].append(start[0].pop(0))
+ for l in sp:
+ pt = t
+ t += l / stotal
+ if sadd and t > sadd[0]:
+ while sadd and sadd[0] < t:
+ nt = (sadd[0] - pt) / (t - pt)
+ bezes = cspbezsplitatlength(s[-1][-1][:],start[0][0][:], nt)
+ s[-1][-1:] = bezes[:2]
+ start[0][0] = bezes[2]
+ pt = sadd.pop(0)
+ s[-1].append(start[0].pop(0))
+ t = 0
+ e = [[]]
+ for sp in elengths:
+ if not end[0]:
+ e.append(end.pop(0))
+ e[-1].append(end[0].pop(0))
+ for l in sp:
+ pt = t
+ t += l / etotal
+ if eadd and t > eadd[0]:
+ while eadd and eadd[0] < t:
+ nt = (eadd[0] - pt) / (t - pt)
+ bezes = cspbezsplitatlength(e[-1][-1][:],end[0][0][:], nt)
+ e[-1][-1:] = bezes[:2]
+ end[0][0] = bezes[2]
+ pt = eadd.pop(0)
+ e[-1].append(end[0].pop(0))
+ start = s[:]
+ end = e[:]
+ else:
+ #which path has fewer segments?
+ lengthdiff = numsegs(start) - numsegs(end)
+ #swap shortest first
+ if lengthdiff > 0:
+ start, end = end, start
+ #subdivide the shorter path
+ for x in range(abs(lengthdiff)):
+ maxlen = 0
+ subpath = 0
+ segment = 0
+ for y in range(len(start)):
+ for z in range(1, len(start[y])):
+ leng = bezlenapprx(start[y][z-1], start[y][z])
+ if leng > maxlen:
+ maxlen = leng
+ subpath = y
+ segment = z
+ sp1, sp2 = start[subpath][segment - 1:segment + 1]
+ start[subpath][segment - 1:segment + 1] = cspbezsplit(sp1, sp2)
+ #if swapped, swap them back
+ if lengthdiff > 0:
+ start, end = end, start
+
+ #break paths so that corresponding subpaths have an equal number of segments
+ s = [[]]
+ e = [[]]
+ while start and end:
+ if start[0] and end[0]:
+ s[-1].append(start[0].pop(0))
+ e[-1].append(end[0].pop(0))
+ elif end[0]:
+ s.append(start.pop(0))
+ e[-1].append(end[0][0])
+ e.append([end[0].pop(0)])
+ elif start[0]:
+ e.append(end.pop(0))
+ s[-1].append(start[0][0])
+ s.append([start[0].pop(0)])
+ else:
+ s.append(start.pop(0))
+ e.append(end.pop(0))
+
+ if self.options.dup:
+ steps = [0] + steps + [1]
+ #create an interpolated path for each interval
+ group = self.document.createElement('svg:g')
+ self.document.documentElement.appendChild(group)
+ for time in steps:
+ interp = []
+ #process subpaths
+ for ssp,esp in zip(s, e):
+ if not (ssp or esp):
+ break
+ interp.append([])
+ #process superpoints
+ for sp,ep in zip(ssp, esp):
+ if not (sp or ep):
+ break
+ interp[-1].append([])
+ #process points
+ for p1,p2 in zip(sp, ep):
+ if not (sp or ep):
+ break
+ interp[-1][-1].append(interppoints(p1, p2, time))
+
+ #remove final subpath if empty.
+ if not interp[-1]:
+ del interp[-1]
+ new = self.document.createElement('svg:path')
+
+ #basic style tweening
+ if self.options.style:
+ basestyle['opacity'] = tweenstylefloat('opacity',sst,est,time)
+ if dostroke:
+ basestyle['stroke-opacity'] = tweenstylefloat('stroke-opacity',sst,est,time)
+ basestyle['stroke-width'] = tweenstyleunit('stroke-width',sst,est,time)
+ basestyle['stroke'] = tweenstylecolor('stroke',sst,est,time)
+ if dofill:
+ basestyle['fill-opacity'] = tweenstylefloat('fill-opacity',sst,est,time)
+ basestyle['fill'] = tweenstylecolor('fill',sst,est,time)
+ new.setAttribute('style', simplestyle.formatStyle(basestyle))
+ new.setAttribute('d', cubicsuperpath.formatPath(interp))
+ group.appendChild(new)
+
+e = Interp()
+e.affect()
diff --git a/share/extensions/kochify.inx b/share/extensions/kochify.inx
new file mode 100644
index 000000000..2e7923f9a
--- /dev/null
+++ b/share/extensions/kochify.inx
@@ -0,0 +1,12 @@
+<inkscape-extension>
+ <_name>Kochify</_name>
+ <id>org.ekips.filter.kochify</id>
+ <dependency type="executable" location="extensions">kochify.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">kochify.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/kochify.py b/share/extensions/kochify.py
new file mode 100755
index 000000000..e6914278c
--- /dev/null
+++ b/share/extensions/kochify.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math, tempfile, copy, cPickle, inkex, simplepath
+
+def findend(d):
+ end = []
+ subPathStart = []
+ for cmd,params in d:
+ if cmd == 'M':
+ subPathStart = params[-2:]
+ if cmd == 'Z':
+ end = subPathStart[:]
+ else:
+ end = params[-2:]
+ return end
+
+class Kochify(inkex.Effect):
+ def effect(self):
+ try:
+ f = open(tempfile.gettempdir() + '/kochify.bin', 'r')
+ proto = cPickle.load(f)
+ f.close()
+ except:
+ return False
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ d = node.attributes.getNamedItem('d')
+ p = simplepath.parsePath(d.value)
+ last = p[0][1][-2:]
+ subPathStart = []
+ cur = []
+ new = []
+ for i in range(len(p)):
+ rep = copy.deepcopy(proto['path'])
+ cmd, params = p[i]
+ if cmd == 'M':
+ subPathStart = params[-2:]
+ new.append(copy.deepcopy(p[i]))
+ continue
+ if cmd == 'Z':
+ cur = subPathStart[:]
+ else:
+ cur = params[-2:]
+
+ if last == cur:
+ continue
+
+ dx = cur[0]-last[0]
+ dy = cur[1]-last[1]
+ length = math.sqrt((dx**2) + (dy**2))
+ angle = math.atan2(dy,dx)
+
+ scale = length / proto['length']
+ rotate = angle - proto['angle']
+ simplepath.scalePath(rep,scale,scale)
+ simplepath.rotatePath(rep, rotate)
+ repend = findend(rep)
+ transx = cur[0] - repend[0]
+ transy = cur[1] - repend[1]
+ simplepath.translatePath(rep, transx, transy)
+
+ if proto['endsinz']:
+ new.extend(rep[:])
+ else:
+ new.extend(rep[1:])
+ last = cur[:]
+
+ d.value = simplepath.formatPath(new)
+e = Kochify()
+e.affect()
diff --git a/share/extensions/kochify_load.inx b/share/extensions/kochify_load.inx
new file mode 100644
index 000000000..b42a32992
--- /dev/null
+++ b/share/extensions/kochify_load.inx
@@ -0,0 +1,12 @@
+<inkscape-extension>
+ <_name>Kochify (Load)</_name>
+ <id>org.ekips.filter.kochify.load</id>
+ <dependency type="executable" location="extensions">kochify_load.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">kochify_load.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/kochify_load.py b/share/extensions/kochify_load.py
new file mode 100755
index 000000000..d50c498c0
--- /dev/null
+++ b/share/extensions/kochify_load.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math, tempfile, cPickle, inkex, simplepath
+
+def findend(d):
+ end = []
+ subPathStart = []
+ for cmd,params in d:
+ if cmd == 'M':
+ subPathStart = params[-2:]
+ if cmd == 'Z':
+ end = subPathStart[:]
+ else:
+ end = params[-2:]
+ return end
+
+class LoadKochify(inkex.Effect):
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ d = simplepath.parsePath(node.attributes.getNamedItem('d').value)
+ start = d[0][1][-2:]
+ end = findend(d)
+ while start == end and len(d):
+ d = d[:-1]
+ end = findend(d)
+ if not end:
+ break
+ dx = end[0]-start[0]
+ dy = end[1]-start[1]
+ length = math.sqrt((dx**2) + (dy**2))
+ angle = math.atan2(dy,dx)
+ endsinz = False
+ if d[-1][0]=='Z':
+ endsinz = True
+ path = {'start': start, 'end': end, 'endsinz': endsinz,
+ 'length': length, 'angle': angle, 'path': d}
+ f = open(tempfile.gettempdir() + '/kochify.bin', 'w')
+ cPickle.dump(path, f)
+ f.close()
+ break
+
+e = LoadKochify()
+e.affect()
diff --git a/share/extensions/lindenmayer.inx b/share/extensions/lindenmayer.inx
new file mode 100644
index 000000000..655c95e33
--- /dev/null
+++ b/share/extensions/lindenmayer.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>Lindenmayer</_name>
+ <id>org.ekips.filter.turtle.lindenmayer</id>
+ <dependency type="executable" location="extensions">lindenmayer.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="order" type="int" min="0" max="100" _gui-text="Order">3</param>
+ <param name="step" type="float" min="0.0" max="1000.0" _gui-text="Step">25.0</param>
+ <param name="angle" type="float" min="0.0" max="360.0" _gui-text="Angle">16.0</param>
+ <param name="axiom" type="string" _gui-text="Axiom">++F</param>
+ <param name="rules" type="string" _gui-text="Rules">F=FF-[-F+F+F]+[+F-F-F]</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">lindenmayer.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/lindenmayer.py b/share/extensions/lindenmayer.py
new file mode 100755
index 000000000..a6848320d
--- /dev/null
+++ b/share/extensions/lindenmayer.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import inkex, simplestyle, pturtle
+
+class LSystem(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-o", "--order",
+ action="store", type="int",
+ dest="order", default=3,
+ help="number of iteration")
+ self.OptionParser.add_option("-a", "--angle",
+ action="store", type="float",
+ dest="angle", default=16.0,
+ help="angle for turn commands")
+ self.OptionParser.add_option("-s", "--step",
+ action="store", type="float",
+ dest="step", default=25.0,
+ help="step size")
+ self.OptionParser.add_option("-x", "--axiom",
+ action="store", type="string",
+ dest="axiom", default="++F",
+ help="initial state of system")
+ self.OptionParser.add_option("-r", "--rules",
+ action="store", type="string",
+ dest="rules", default="F=FF-[-F+F+F]+[+F-F-F]",
+ help="replacement rules")
+ self.stack = []
+ self.turtle = pturtle.pTurtle()
+ def iterate(self):
+ self.rules = dict([i.split("=") for i in self.options.rules.upper().split(";") if i.count("=")==1])
+ self.__recurse(self.options.axiom.upper(),0)
+ return self.turtle.getPath()
+ def __recurse(self,rule,level):
+ for c in rule:
+ if level < self.options.order:
+ try:
+ self.__recurse(self.rules[c],level+1)
+ except KeyError:
+ pass
+
+ if c == 'F':
+ self.turtle.pd()
+ self.turtle.fd(self.options.step)
+ elif c == 'G':
+ self.turtle.pu()
+ self.turtle.fd(self.options.step)
+ elif c == '+':
+ self.turtle.lt(self.options.angle)
+ elif c == '-':
+ self.turtle.rt(self.options.angle)
+ elif c == '|':
+ self.turtle.lt(180)
+ elif c == '[':
+ self.stack.append([self.turtle.getpos(), self.turtle.getheading()])
+ elif c == ']':
+ self.turtle.pu()
+ pos,heading = self.stack.pop()
+ self.turtle.setpos(pos)
+ self.turtle.setheading(heading)
+ def effect(self):
+ new = self.document.createElement('svg:path')
+ s = {'stroke-linejoin': 'miter', 'stroke-width': '1.0px',
+ 'stroke-opacity': '1.0', 'fill-opacity': '1.0',
+ 'stroke': '#000000', 'stroke-linecap': 'butt',
+ 'fill': 'none'}
+ new.setAttribute('style', simplestyle.formatStyle(s))
+ new.setAttribute('d', self.iterate())
+ self.document.documentElement.appendChild(new)
+
+e = LSystem()
+e.affect()
diff --git a/share/extensions/motion.inx b/share/extensions/motion.inx
new file mode 100644
index 000000000..46f7e7082
--- /dev/null
+++ b/share/extensions/motion.inx
@@ -0,0 +1,14 @@
+<inkscape-extension>
+ <_name>Motion</_name>
+ <id>org.ekips.filter.motion</id>
+ <dependency type="executable" location="extensions">motion.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="magnitude" type="float" min="0" max="1000" _gui-text="Magnitude">100</param>
+ <param name="angle" type="float" min="0.0" max="360.0" _gui-text="Direction">45</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">motion.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/motion.py b/share/extensions/motion.py
new file mode 100755
index 000000000..95100e629
--- /dev/null
+++ b/share/extensions/motion.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math, inkex, simplestyle, simplepath, bezmisc
+
+class Motion(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-a", "--angle",
+ action="store", type="float",
+ dest="angle", default=45.0,
+ help="direction of the motion vector")
+ self.OptionParser.add_option("-m", "--magnitude",
+ action="store", type="float",
+ dest="magnitude", default=100.0,
+ help="magnitude of the motion vector")
+
+ def makeface(self,last,(cmd, params)):
+ a = []
+ a.append(['M',last[:]])
+ a.append([cmd, params[:]])
+
+ #translate path segment along vector
+ np = params[:]
+ defs = simplepath.pathdefs[cmd]
+ for i in range(defs[1]):
+ if defs[3][i] == 'x':
+ np[i] += self.vx
+ elif defs[3][i] == 'y':
+ np[i] += self.vy
+
+ a.append(['L',[np[-2],np[-1]]])
+
+ #reverse direction of path segment
+ np[-2:] = last[0]+self.vx,last[1]+self.vy
+ if cmd == 'C':
+ c1 = np[:2], np[2:4] = np[2:4], np[:2]
+ a.append([cmd,np[:]])
+
+ a.append(['Z',[]])
+ face = self.document.createElement('svg:path')
+ self.facegroup.appendChild(face)
+ face.setAttribute('d', simplepath.formatPath(a))
+
+
+ def effect(self):
+ self.vx = math.cos(math.radians(self.options.angle))*self.options.magnitude
+ self.vy = math.sin(math.radians(self.options.angle))*self.options.magnitude
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ group = self.document.createElement('svg:g')
+ self.facegroup = self.document.createElement('svg:g')
+ node.parentNode.appendChild(group)
+ group.appendChild(self.facegroup)
+ group.appendChild(node)
+
+ try:
+ t = node.attributes.getNamedItem('transform').value
+ group.setAttribute('transform', t)
+ node.attributes.getNamedItem('transform').value=""
+ except AttributeError:
+ pass
+
+ s = node.attributes.getNamedItem('style').value
+ self.facegroup.setAttribute('style', s)
+
+ p = simplepath.parsePath(node.attributes.getNamedItem('d').value)
+ for cmd,params in p:
+ tees = []
+ if cmd == 'C':
+ bez = (last,params[:2],params[2:4],params[-2:])
+ tees = [t for t in bezmisc.beziertatslope(bez,(self.vy,self.vx)) if 0<t<1]
+ tees.sort()
+
+ segments = []
+ if len(tees) == 0 and cmd in ['L','C']:
+ segments.append([cmd,params[:]])
+ elif len(tees) == 1:
+ one,two = bezmisc.beziersplitatt(bez,tees[0])
+ segments.append([cmd,list(one[1]+one[2]+one[3])])
+ segments.append([cmd,list(two[1]+two[2]+two[3])])
+ elif len(tees) == 2:
+ one,two = bezmisc.beziersplitatt(bez,tees[0])
+ two,three = bezmisc.beziersplitatt(two,tees[1])
+ segments.append([cmd,list(one[1]+one[2]+one[3])])
+ segments.append([cmd,list(two[1]+two[2]+two[3])])
+ segments.append([cmd,list(three[1]+three[2]+three[3])])
+
+ for seg in segments:
+ self.makeface(last,seg)
+ last = seg[1][-2:]
+
+ if cmd == 'M':
+ subPathStart = params[-2:]
+ if cmd == 'Z':
+ last = subPathStart
+ else:
+ last = params[-2:]
+
+
+
+
+e = Motion()
+e.affect()
diff --git a/share/extensions/pdf_output.inx b/share/extensions/pdf_output.inx
new file mode 100644
index 000000000..868a004be
--- /dev/null
+++ b/share/extensions/pdf_output.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>PDF Output</_name>
+ <id>org.inkscape.output.pdf</id>
+ <dependency type="extension">org.inkscape.output.ps</dependency>
+ <dependency type="executable" location="extensions">ps2pdf.sh</dependency>
+ <dependency type="executable">ps2pdf</dependency>
+ <output>
+ <extension>.pdf</extension>
+ <mimetype>image/x-portable-document-format</mimetype>
+ <_filetypename>Adobe PDF (*.pdf)</_filetypename>
+ <_filetypetooltip>Adobe Portable Document Format</_filetypetooltip>
+ </output>
+ <script>
+ <command reldir="extensions">ps2pdf.sh</command>
+ <helper_extension>org.inkscape.output.ps</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/pdf_output_via_gs_on_win32.inx b/share/extensions/pdf_output_via_gs_on_win32.inx
new file mode 100644
index 000000000..50e63ced8
--- /dev/null
+++ b/share/extensions/pdf_output_via_gs_on_win32.inx
@@ -0,0 +1,24 @@
+<!--
+This requires ghostscript in order to work, but there isn't yet
+a suitable way to test for ghostscript's presence.
+
+TODO: add this file and ps2pdf.cmd to the makefile once this is fixed so
+that they get into the tarball
+-->
+<inkscape-extension>
+ <_name>PDF Output</_name>
+ <id>org.inkscape.output.pdf.via_gs_on_win32</id>
+ <dependency type="extension">org.inkscape.output.ps</dependency>
+ <dependency type="executable" location="extensions">ps2pdf.cmd</dependency>
+ <dependency type="executable" location="path">cmd.exe</dependency>
+ <output>
+ <extension>.pdf</extension>
+ <mimetype>image/x-portable-document-format</mimetype>
+ <_filetypename>Adobe PDF (*.pdf)</_filetypename>
+ <_filetypetooltip>Adobe Portable Document Format</_filetypetooltip>
+ </output>
+ <script>
+ <command reldir="extensions">ps2pdf.cmd</command>
+ <helper_extension>org.inkscape.output.ps</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/ps2dxf.sh b/share/extensions/ps2dxf.sh
new file mode 100644
index 000000000..3479800e1
--- /dev/null
+++ b/share/extensions/ps2dxf.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+TMPDIR="${TMPDIR-/tmp}"
+TEMPFILENAME=`mktemp 2>/dev/null || echo "$TMPDIR/tmp-ps-$$.dxf"`
+pstoedit -f dxf "$1" "${TEMPFILENAME}" > /dev/null 2>&1
+rc=0
+cat < "${TEMPFILENAME}" || rc=1
+rm -f "${TEMPFILENAME}"
+exit $rc
+d \ No newline at end of file
diff --git a/share/extensions/ps2epsi.sh b/share/extensions/ps2epsi.sh
new file mode 100755
index 000000000..3584bcde8
--- /dev/null
+++ b/share/extensions/ps2epsi.sh
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+TMPDIR="${TMPDIR-/tmp}"
+TEMPFILENAME=`mktemp 2>/dev/null || echo "$TMPDIR/tmp-ps-$$.epsi"`
+ps2epsi "$1" "${TEMPFILENAME}" > /dev/null 2>&1
+rc=0
+cat < "${TEMPFILENAME}" || rc=1
+rm -f "${TEMPFILENAME}"
+exit $rc
diff --git a/share/extensions/ps2pdf.cmd b/share/extensions/ps2pdf.cmd
new file mode 100644
index 000000000..81e8ba813
--- /dev/null
+++ b/share/extensions/ps2pdf.cmd
@@ -0,0 +1,10 @@
+REM BEGIN
+@echo off
+REM edit %GSDIR% to match the ghostscript installation directory
+set GSDIR=%PROGRAMFILES%\gs\gs8.51
+set GSBINDIR=%GSDIR%\bin
+set GSLIBDIR=%GSDIR%\lib
+set PATH=%GSBINDIR%;%GSLIBDIR%;%PATH%
+echo %PATH%
+ps2pdf.bat %1 -
+REM END \ No newline at end of file
diff --git a/share/extensions/ps2pdf.sh b/share/extensions/ps2pdf.sh
new file mode 100755
index 000000000..86daa96bc
--- /dev/null
+++ b/share/extensions/ps2pdf.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+exec ps2pdf "$1" - 2> /dev/null
diff --git a/share/extensions/ps_input.inx b/share/extensions/ps_input.inx
new file mode 100644
index 000000000..84d6907ac
--- /dev/null
+++ b/share/extensions/ps_input.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>Postscript Input</_name>
+ <id>org.inkscape.input.ps</id>
+ <dependency type="extension">org.inkscape.input.sk</dependency>
+ <dependency type="executable">pstoedit</dependency>
+ <input>
+ <extension>.ps</extension>
+ <mimetype>image/x-postscript</mimetype>
+ <_filetypename>Postscript (*.ps)</_filetypename>
+ <_filetypetooltip>Postscript</_filetypetooltip>
+ <output_extension>org.inkscape.output.ps</output_extension>
+ </input>
+ <script>
+ <command reldir="path">pstoedit -f sk</command>
+ <helper_extension>org.inkscape.input.sk</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/pturtle.py b/share/extensions/pturtle.py
new file mode 100755
index 000000000..f1806a68b
--- /dev/null
+++ b/share/extensions/pturtle.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math
+class pTurtle:
+ '''A Python path turtle'''
+ def __init__(self, home=(0,0)):
+ self.__home = [home[0], home[1]]
+ self.__pos = self.__home[:]
+ self.__heading = -90
+ self.__path = ""
+ self.__draw = True
+ self.__new = True
+ def forward(self,mag):
+ self.setpos((self.__pos[0] + math.cos(math.radians(self.__heading))*mag,
+ self.__pos[1] + math.sin(math.radians(self.__heading))*mag))
+ def backward(self,mag):
+ self.setpos((self.__pos[0] - math.cos(math.radians(self.__heading))*mag,
+ self.__pos[1] - math.sin(math.radians(self.__heading))*mag))
+ def right(self,deg):
+ self.__heading -= deg
+ def left(self,deg):
+ self.__heading += deg
+ def penup(self):
+ self.__draw = False
+ self.__new = False
+ def pendown(self):
+ if not self.__draw:
+ self.__new = True
+ self.__draw = True
+ def pentoggle(self):
+ if self.__draw:
+ self.penup()
+ else:
+ self.pendown()
+ def home(self):
+ self.setpos(self.__home)
+ def clean(self):
+ self.__path = ''
+ def clear(self):
+ self.clean()
+ self.home()
+ def setpos(self,(x,y)):
+ if self.__new:
+ self.__path += "M"+",".join([str(i) for i in self.__pos])
+ self.__new = False
+ self.__pos = [x, y]
+ if self.__draw:
+ self.__path += "L"+",".join([str(i) for i in self.__pos])
+ def getpos(self):
+ return self.__pos[:]
+ def setheading(self,deg):
+ self.__heading = deg
+ def getheading(self):
+ return self.__heading
+ def sethome(self,(x,y)):
+ self.__home = [x, y]
+ def getPath(self):
+ return self.__path
+ fd = forward
+ bk = backward
+ rt = right
+ lt = left
+ pu = penup
+ pd = pendown
diff --git a/share/extensions/radiusrand.inx b/share/extensions/radiusrand.inx
new file mode 100644
index 000000000..555785477
--- /dev/null
+++ b/share/extensions/radiusrand.inx
@@ -0,0 +1,15 @@
+<inkscape-extension>
+ <_name>Radius Randomize</_name>
+ <id>org.ekips.filter.radiusrand</id>
+ <dependency type="executable" location="extensions">radiusrand.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="radius" type="float" min="0.0" max="1000.0" _gui-text="Radius">100.0</param>
+ <param name="end" type="boolean" _gui-text="Randomize Nodes">true</param>
+ <param name="ctrl" type="boolean" _gui-text="Randomize Control Points">false</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">radiusrand.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/radiusrand.py b/share/extensions/radiusrand.py
new file mode 100755
index 000000000..97e9aed41
--- /dev/null
+++ b/share/extensions/radiusrand.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import random, math, inkex, cubicsuperpath
+
+def randomize((x, y), r):
+ r = random.uniform(0.0,r)
+ a = random.uniform(0.0,2*math.pi)
+ x += math.cos(a)*r
+ y += math.sin(a)*r
+ return [x, y]
+
+class RadiusRandomize(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-r", "--radius",
+ action="store", type="float",
+ dest="radius", default=10.0,
+ help="Randomly move control and end points in this radius")
+ self.OptionParser.add_option("-c", "--ctrl",
+ action="store", type="inkbool",
+ dest="ctrl", default=True,
+ help="Randomize control points")
+ self.OptionParser.add_option("-e", "--end",
+ action="store", type="inkbool",
+ dest="end", default=True,
+ help="Randomize nodes")
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ d = node.attributes.getNamedItem('d')
+ p = cubicsuperpath.parsePath(d.value)
+ for subpath in p:
+ for csp in subpath:
+ if self.options.end:
+ delta=randomize([0,0], self.options.radius)
+ csp[0][0]+=delta[0]
+ csp[0][1]+=delta[1]
+ csp[1][0]+=delta[0]
+ csp[1][1]+=delta[1]
+ csp[2][0]+=delta[0]
+ csp[2][1]+=delta[1]
+ if self.options.ctrl:
+ csp[0]=randomize(csp[0], self.options.radius)
+ csp[2]=randomize(csp[2], self.options.radius)
+ d.value = cubicsuperpath.formatPath(p)
+
+e = RadiusRandomize()
+e.affect()
+
diff --git a/share/extensions/randompnt.inx b/share/extensions/randompnt.inx
new file mode 100644
index 000000000..36086e762
--- /dev/null
+++ b/share/extensions/randompnt.inx
@@ -0,0 +1,11 @@
+<inkscape-extension>
+ <_name>Random Point</_name>
+ <id>org.inkscape.effect.randompnt</id>
+ <dependency type="plugin" location="plugins">randompnt</dependency>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <plugin>
+ <name>randompnt</name>
+ </plugin>
+</inkscape-extension>
diff --git a/share/extensions/randompos.inx b/share/extensions/randompos.inx
new file mode 100644
index 000000000..3c677382a
--- /dev/null
+++ b/share/extensions/randompos.inx
@@ -0,0 +1,11 @@
+<inkscape-extension>
+ <_name>Random Position</_name>
+ <id>org.inkscape.effect.randompos</id>
+ <dependency type="plugin" location="plugins">randompos</dependency>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <plugin>
+ <name>randompos</name>
+ </plugin>
+</inkscape-extension>
diff --git a/share/extensions/rtree.inx b/share/extensions/rtree.inx
new file mode 100644
index 000000000..9832725d9
--- /dev/null
+++ b/share/extensions/rtree.inx
@@ -0,0 +1,14 @@
+<inkscape-extension>
+ <_name>Random Tree</_name>
+ <id>org.ekips.filter.turtle.rtree</id>
+ <dependency type="executable" location="extensions">rtree.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="size" type="float" min="0.0" max="1000.0" _gui-text="Initial Size">100.0</param>
+ <param name="minimum" type="float" min="0.0" max="500.0" _gui-text="Minimum Size">40.0</param>
+ <effect>
+ <object-type>all</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">rtree.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/rtree.py b/share/extensions/rtree.py
new file mode 100755
index 000000000..8728ed7a3
--- /dev/null
+++ b/share/extensions/rtree.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import inkex, simplestyle, pturtle, random
+
+def rtree(turtle, size, min):
+ if size < min:
+ return
+ turtle.fd(size)
+ turn = random.uniform(20, 40)
+ turtle.lt(turn)
+ rtree(turtle, size*random.uniform(0.5,0.9), min)
+ turtle.rt(turn)
+ turn = random.uniform(20, 40)
+ turtle.rt(turn)
+ rtree(turtle, size*random.uniform(0.5,0.9), min)
+ turtle.lt(turn)
+ turtle.bk(size)
+
+class RTreeTurtle(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-s", "--size",
+ action="store", type="float",
+ dest="size", default=100.0,
+ help="initial branch size")
+ self.OptionParser.add_option("-m", "--minimum",
+ action="store", type="float",
+ dest="minimum", default=4.0,
+ help="minimum branch size")
+ def effect(self):
+ new = self.document.createElement('svg:path')
+ s = {'stroke-linejoin': 'miter', 'stroke-width': '1.0px',
+ 'stroke-opacity': '1.0', 'fill-opacity': '1.0',
+ 'stroke': '#000000', 'stroke-linecap': 'butt',
+ 'fill': 'none'}
+ new.setAttribute('style', simplestyle.formatStyle(s))
+ t = pturtle.pTurtle()
+ rtree(t, self.options.size, self.options.minimum)
+ new.setAttribute('d', t.getPath())
+ self.document.documentElement.appendChild(new)
+
+e = RTreeTurtle()
+e.affect()
diff --git a/share/extensions/simplepath.py b/share/extensions/simplepath.py
new file mode 100755
index 000000000..06ed73672
--- /dev/null
+++ b/share/extensions/simplepath.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+"""
+simplepath.py
+functions for digesting paths into a simple list structure
+
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""
+import re, math
+
+def lexPath(d):
+ """
+ returns and iterator that breaks path data
+ identifies command and parameter tokens
+ """
+ offset = 0
+ length = len(d)
+ delim = re.compile(r'[ \t\r\n,]+')
+ command = re.compile(r'[MLHVCSQTAZmlhvcsqtaz]')
+ parameter = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
+ while 1:
+ m = delim.match(d, offset)
+ if m:
+ offset = m.end()
+ if offset >= length:
+ break
+ m = command.match(d, offset)
+ if m:
+ yield [d[offset:m.end()], True]
+ offset = m.end()
+ continue
+ m = parameter.match(d, offset)
+ if m:
+ yield [d[offset:m.end()], False]
+ offset = m.end()
+ continue
+ #TODO: create new exception
+ raise Exception, 'Invalid path data!'
+'''
+pathdefs = {commandfamily:
+ [
+ implicitnext,
+ #params,
+ [casts,cast,cast],
+ [coord type,x,y,0]
+ ]}
+'''
+pathdefs = {
+ 'M':['L', 2, [float, float], ['x','y']],
+ 'L':['L', 2, [float, float], ['x','y']],
+ 'H':['H', 1, [float], ['x']],
+ 'V':['V', 1, [float], ['y']],
+ 'C':['C', 6, [float, float, float, float, float, float], ['x','y','x','y','x','y']],
+ 'S':['S', 4, [float, float, float, float], ['x','y','x','y']],
+ 'Q':['Q', 4, [float, float, float, float], ['x','y','x','y']],
+ 'T':['T', 2, [float, float], ['x','y']],
+ 'A':['A', 7, [float, float, float, int, int, float, float], [0,0,0,0,0,'x','y']],
+ 'Z':['L', 0, [], []]
+ }
+def parsePath(d):
+ """
+ Parse SVG path and return an array of segments.
+ Removes all shorthand notation.
+ Converts coordinates to absolute.
+ """
+ retval = []
+ lexer = lexPath(d)
+
+ pen = (0.0,0.0)
+ subPathStart = pen
+ lastControl = pen
+ lastCommand = ''
+
+ while 1:
+ try:
+ token, isCommand = lexer.next()
+ except StopIteration:
+ break
+ params = []
+ needParam = True
+ if isCommand:
+ if not lastCommand and token.upper() != 'M':
+ raise Exception, 'Invalid path, must begin with moveto.'
+ else:
+ command = token
+ else:
+ #command was omited
+ #use last command's implicit next command
+ needParam = False
+ if lastCommand:
+ if token.isupper():
+ command = pathdefs[lastCommand.upper()][0]
+ else:
+ command = pathdefs[lastCommand.upper()][0].lower()
+ else:
+ raise Exception, 'Invalid path, no initial command.'
+ numParams = pathdefs[command.upper()][1]
+ while numParams > 0:
+ if needParam:
+ try:
+ token, isCommand = lexer.next()
+ if isCommand:
+ raise Exception, 'Invalid number of parameters'
+ except StopIteration:
+ raise Exception, 'Unexpected end of path'
+ cast = pathdefs[command.upper()][2][-numParams]
+ param = cast(token)
+ if command.islower():
+ if pathdefs[command.upper()][3][-numParams]=='x':
+ param += pen[0]
+ elif pathdefs[command.upper()][3][-numParams]=='y':
+ param += pen[1]
+ params.append(param)
+ needParam = True
+ numParams -= 1
+ #segment is now absolute so
+ outputCommand = command.upper()
+
+ #Flesh out shortcut notation
+ if outputCommand in ('H','V'):
+ if outputCommand == 'H':
+ params.append(pen[1])
+ if outputCommand == 'V':
+ params.insert(0,pen[0])
+ outputCommand = 'L'
+ if outputCommand in ('S','T'):
+ params.insert(0,pen[1]+(pen[1]-lastControl[1]))
+ params.insert(0,pen[0]+(pen[0]-lastControl[0]))
+ if outputCommand == 'S':
+ outputCommand = 'C'
+ if outputCommand == 'T':
+ outputCommand = 'Q'
+
+ #current values become "last" values
+ if outputCommand == 'M':
+ subPathStart = tuple(params[0:2])
+ if outputCommand == 'Z':
+ pen = subPathStart
+ else:
+ pen = tuple(params[-2:])
+
+ if outputCommand in ('Q','C'):
+ lastControl = tuple(params[-4:-2])
+ else:
+ lastControl = pen
+ lastCommand = command
+
+ retval.append([outputCommand,params])
+ return retval
+
+def formatPath(a):
+ """Format SVG path data from an array"""
+ return "".join([cmd + " ".join([str(p) for p in params]) for cmd, params in a])
+
+def translatePath(p, x, y):
+ for cmd,params in p:
+ defs = pathdefs[cmd]
+ for i in range(defs[1]):
+ if defs[3][i] == 'x':
+ params[i] += x
+ elif defs[3][i] == 'y':
+ params[i] += y
+
+def scalePath(p, x, y):
+ for cmd,params in p:
+ defs = pathdefs[cmd]
+ for i in range(defs[1]):
+ if defs[3][i] == 'x':
+ params[i] *= x
+ elif defs[3][i] == 'y':
+ params[i] *= y
+
+def rotatePath(p, a, cx = 0, cy = 0):
+ if a == 0:
+ return p
+ for cmd,params in p:
+ defs = pathdefs[cmd]
+ for i in range(defs[1]):
+ if defs[3][i] == 'x':
+ x = params[i] - cx
+ y = params[i + 1] - cy
+ r = math.sqrt((x**2) + (y**2))
+ if r != 0:
+ theta = math.atan2(y, x) + a
+ params[i] = (r * math.cos(theta)) + cx
+ params[i + 1] = (r * math.sin(theta)) + cy
+
diff --git a/share/extensions/simplestyle.py b/share/extensions/simplestyle.py
new file mode 100755
index 000000000..32fd987b7
--- /dev/null
+++ b/share/extensions/simplestyle.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+"""
+simplestyle.py
+Two simple functions for working with inline css
+
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+def parseStyle(s):
+ """Create a dictionary from the value of an inline style attribute"""
+ return dict([i.split(":") for i in s.split(";")])
+def formatStyle(a):
+ """Format an inline style attribute from a dictionary"""
+ return ";".join([":".join(i) for i in a.iteritems()])
diff --git a/share/extensions/sk2svg.sh b/share/extensions/sk2svg.sh
new file mode 100755
index 000000000..dbfb3d1f7
--- /dev/null
+++ b/share/extensions/sk2svg.sh
@@ -0,0 +1,12 @@
+#! /bin/sh
+
+rc=0
+
+TMPDIR="${TMPDIR-/tmp}"
+UNIQTMPDIR=`mktemp -d 2>/dev/null || (mkdir "$TMPDIR/sk2svg.$$" && echo "$TMPDIR/sk2svg.$$") || echo "$TMPDIR"`
+TMPSVG="$UNIQTMPDIR/sk2svg$$.svg"
+skconvert "$1" "$TMPSVG" > /dev/null 2>&1 || rc=1
+cat < "$TMPSVG" || RC=1
+rm -f "$TMPSVG"
+[ "$UNIQTMPDIR" = "$TMPDIR" ] || rmdir "$UNIQTMPDIR"
+exit $rc
diff --git a/share/extensions/sk_input.inx b/share/extensions/sk_input.inx
new file mode 100644
index 000000000..e64e8ef0c
--- /dev/null
+++ b/share/extensions/sk_input.inx
@@ -0,0 +1,15 @@
+<inkscape-extension>
+ <_name>Sketch Input</_name>
+ <id>org.inkscape.input.sk</id>
+ <dependency type="executable" location="extensions">sk2svg.sh</dependency>
+ <dependency type="executable">skconvert</dependency>
+ <input>
+ <extension>.sk</extension>
+ <mimetype>application/x-sketch</mimetype>
+ <_filetypename>Sketch Diagram (*.sk)</_filetypename>
+ <_filetypetooltip>A diagram created with the program Sketch</_filetypetooltip>
+ </input>
+ <script>
+ <command reldir="extensions">sk2svg.sh</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/straightseg.inx b/share/extensions/straightseg.inx
new file mode 100644
index 000000000..490efd187
--- /dev/null
+++ b/share/extensions/straightseg.inx
@@ -0,0 +1,14 @@
+<inkscape-extension>
+ <_name>Segment Straightener</_name>
+ <id>org.ekips.filter.straightseg</id>
+ <dependency type="executable" location="extensions">straightseg.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="percent" type="float" min="0.0" max="100.0" _gui-text="Percent">50.0</param>
+ <param name="behavior" type="int" min="1" max="2" _gui-text="Behavior">1</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">straightseg.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/straightseg.py b/share/extensions/straightseg.py
new file mode 100755
index 000000000..692160e4d
--- /dev/null
+++ b/share/extensions/straightseg.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math, inkex, simplepath, sys
+
+def pointAtPercent((x1, y1), (x2, y2), percent):
+ percent /= 100.0
+ x = x1 + (percent * (x2 - x1))
+ y = y1 + (percent * (y2 - y1))
+ return [x,y]
+
+class SegmentStraightener(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-p", "--percent",
+ action="store", type="float",
+ dest="percent", default=10.0,
+ help="move curve handles PERCENT percent closer to a straight line")
+ self.OptionParser.add_option("-b", "--behavior",
+ action="store", type="int",
+ dest="behave", default=1,
+ help="straightening behavior for cubic segments")
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'path':
+ d = node.attributes.getNamedItem('d')
+ p = simplepath.parsePath(d.value)
+ last = []
+ subPathStart = []
+ for cmd,params in p:
+ if cmd == 'C':
+ if self.options.behave <= 1:
+ #shorten handles towards end points
+ params[:2] = pointAtPercent(params[:2],last[:],self.options.percent)
+ params[2:4] = pointAtPercent(params[2:4],params[-2:],self.options.percent)
+ else:
+ #shorten handles towards thirds of the segment
+ dest1 = pointAtPercent(last[:],params[-2:],33.3)
+ dest2 = pointAtPercent(params[-2:],last[:],33.3)
+ params[:2] = pointAtPercent(params[:2],dest1[:],self.options.percent)
+ params[2:4] = pointAtPercent(params[2:4],dest2[:],self.options.percent)
+ elif cmd == 'Q':
+ dest = pointAtPercent(last[:],params[-2:],50)
+ params[:2] = pointAtPercent(params[:2],dest,self.options.percent)
+ if cmd == 'M':
+ subPathStart = params[-2:]
+ if cmd == 'Z':
+ last = subPathStart[:]
+ else:
+ last = params[-2:]
+ d.value = simplepath.formatPath(p)
+
+e = SegmentStraightener()
+e.affect()
diff --git a/share/extensions/summersnight.inx b/share/extensions/summersnight.inx
new file mode 100644
index 000000000..82b140b26
--- /dev/null
+++ b/share/extensions/summersnight.inx
@@ -0,0 +1,12 @@
+<inkscape-extension>
+ <_name>Summer's Night</_name>
+ <id>org.ekips.filter.summersnight</id>
+ <dependency type="executable" location="extensions">summersnight.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">summersnight.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/summersnight.py b/share/extensions/summersnight.py
new file mode 100755
index 000000000..7ddfdf275
--- /dev/null
+++ b/share/extensions/summersnight.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+"""
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+"""
+import inkex, os, re, simplepath, cubicsuperpath
+from ffgeom import *
+
+uuconv = {'in':90.0, 'pt':1.25, 'px':1, 'mm':3.5433070866, 'cm':35.433070866, 'pc':15.0}
+def unittouu(string):
+ unit = re.compile('(%s)$' % '|'.join(uuconv.keys()))
+ param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)')
+
+ p = param.match(string)
+ u = unit.search(string)
+ if p:
+ retval = float(p.string[p.start():p.end()])
+ else:
+ retval = 0.0
+ if u:
+ try:
+ return retval * uuconv[u.string[u.start():u.end()]]
+ except KeyError:
+ pass
+ return retval
+
+class Project(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ def effect(self):
+ if len(self.options.ids) < 2:
+ inkex.debug("Requires two selected paths. The second must be exctly four nodes long.")
+ exit()
+
+ #obj is selected second
+ obj = self.selected[self.options.ids[0]]
+ trafo = self.selected[self.options.ids[1]]
+ if obj.tagName == 'path' and trafo.tagName == 'path':
+ #distil trafo into four node points
+ trafo = cubicsuperpath.parsePath(trafo.attributes.getNamedItem('d').value)
+ trafo = [[Point(csp[1][0],csp[1][1]) for csp in subs] for subs in trafo][0][:4]
+
+ #vectors pointing away from the trafo origin
+ self.t1 = Segment(trafo[0],trafo[1])
+ self.t2 = Segment(trafo[1],trafo[2])
+ self.t3 = Segment(trafo[3],trafo[2])
+ self.t4 = Segment(trafo[0],trafo[3])
+
+ #query inkscape about the bounding box of obj
+ self.q = {'x':0,'y':0,'width':0,'height':0}
+ file = self.args[-1]
+ id = self.options.ids[0]
+ for query in self.q.keys():
+ f = os.popen("inkscape --query-%s --query-id=%s %s" % (query,id,file))
+ self.q[query] = float(f.read())
+ f.close()
+ #glean document height from the SVG
+ docheight = unittouu(inkex.xml.xpath.Evaluate('/svg/@height',self.document)[0].value)
+ #Flip inkscapes transposed renderer coords
+ self.q['y'] = docheight - self.q['y'] - self.q['height']
+
+ #process path
+ d = obj.attributes.getNamedItem('d')
+ p = cubicsuperpath.parsePath(d.value)
+ for subs in p:
+ for csp in subs:
+ csp[0] = self.trafopoint(csp[0])
+ csp[1] = self.trafopoint(csp[1])
+ csp[2] = self.trafopoint(csp[2])
+ d.value = cubicsuperpath.formatPath(p)
+
+ def trafopoint(self,(x,y)):
+ #Transform algorithm thanks to Jose Hevia (freon)
+ vector = Segment(Point(self.q['x'],self.q['y']),Point(x,y))
+ xratio = abs(vector.delta_x())/self.q['width']
+ yratio = abs(vector.delta_y())/self.q['height']
+
+ horz = Segment(self.t1.pointAtRatio(xratio),self.t3.pointAtRatio(xratio))
+ vert = Segment(self.t4.pointAtRatio(yratio),self.t2.pointAtRatio(yratio))
+
+ p = intersectSegments(vert,horz)
+ return [p['x'],p['y']]
+
+e = Project()
+e.affect()
diff --git a/share/extensions/svg_and_media_zip_output.inx b/share/extensions/svg_and_media_zip_output.inx
new file mode 100644
index 000000000..95863aec3
--- /dev/null
+++ b/share/extensions/svg_and_media_zip_output.inx
@@ -0,0 +1,18 @@
+<inkscape-extension>
+ <_name>ZIP Output</_name>
+ <id>org.inkscape.output.ZIP</id>
+ <dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
+ <dependency type="executable" location="extensions">svg_and_media_zip_output.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <output>
+ <extension>.zip</extension>
+ <mimetype>application/x-zip</mimetype>
+ <_filetypename>Compressed Inkscape SVG with media (*.zip)</_filetypename>
+ <_filetypetooltip>Inkscape's native file format compressed with Zip and including all media files</_filetypetooltip>
+ <dataloss>FALSE</dataloss>
+ </output>
+ <script>
+ <command reldir="extensions" interpreter="python">svg_and_media_zip_output.py</command>
+ <helper_extension>org.inkscape.output.svg.inkscape</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/svg_and_media_zip_output.py b/share/extensions/svg_and_media_zip_output.py
new file mode 100644
index 000000000..09ecb5f99
--- /dev/null
+++ b/share/extensions/svg_and_media_zip_output.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+"""
+svg_and_media_zip_output.py
+An extention which collects all images to the documents directory and
+creates a zip archive containing all images and the document
+
+Copyright (C) 2005 Pim Snel, pim@lingewoud.com
+this is the first Python script ever created
+its based on embedimage.py
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Version 0.3
+
+TODO
+- fix bug: not saving existing .zip after a Collect for Output is run
+ this bug occurs because after running an effect extention the inkscape:output_extension is reset to svg.inkscape
+ the file name is still xxx.zip. after saving again the file xxx.zip is written with a plain .svg which
+ looks like a corrupt zip
+- maybe add better extention
+"""
+
+import inkex, os.path
+import os
+import string
+import zipfile
+import shutil
+import sys
+import tempfile
+
+class MyEffect(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+ self.documentDst=None
+
+ def parseTmp(self,file=None):
+ """Parse document in specified file or on stdin"""
+ reader = inkex.xml.dom.ext.reader.Sax2.Reader()
+ try:
+ try:
+ stream = open(file,'r')
+ except:
+ stream = open(self.args[-1],'r')
+ except:
+ stream = sys.stdin
+ self.documentDst = reader.fromStream(stream)
+ stream.close()
+
+ def output(self):
+ pass
+
+ def effect(self):
+
+ #get needed info from orig document
+ ctx_orig = inkex.xml.xpath.Context.Context(self.document,processorNss=inkex.NSS)
+
+ ttmp_orig = inkex.xml.xpath.Evaluate('/svg',self.document, context=ctx_orig)
+
+ docbase = ttmp_orig[0].attributes.getNamedItemNS(inkex.NSS[u'sodipodi'],'docbase')
+ docname = ttmp_orig[0].attributes.getNamedItemNS(inkex.NSS[u'sodipodi'],'docname')
+
+ orig_tmpfile = sys.argv[1]
+
+ # create destination zip in same directory as the document
+ z = zipfile.ZipFile(docbase.value + '/'+ docname.value + '.zip', 'w')
+
+ #create os temp dir
+ tmp_dir = tempfile.mkdtemp()
+
+ #fixme replace whatever extention
+ docstripped = docname.value.replace('.zip', '')
+
+ #read tmpdoc and copy all images to temp dir
+ for node in inkex.xml.xpath.Evaluate('//image',self.document, context=ctx_orig):
+ self.collectAndZipImages(node, tmp_dir, docname, z)
+
+ ##copy tmpdoc to tempdir
+ dst_file = os.path.join(tmp_dir, docstripped)
+ stream = open(dst_file,'w')
+
+ inkex.xml.dom.ext.Print(self.document,stream)
+
+ stream.close()
+
+ z.write(dst_file.encode("latin-1"),docstripped.encode("latin-1")+'.svg')
+ z.close()
+
+ shutil.move(docbase.value + '/'+ docname.value + '.zip',docbase.value + '/'+ docname.value)
+
+ shutil.rmtree(tmp_dir)
+
+ def collectAndZipImages(self, node, tmp_dir, docname, z):
+ xlink = node.attributes.getNamedItemNS(inkex.NSS[u'xlink'],'href')
+ if (xlink.value[:4]!='data'):
+ absref=node.attributes.getNamedItemNS(inkex.NSS[u'sodipodi'],'absref')
+
+ if (os.path.isfile(absref.value)):
+ shutil.copy(absref.value,tmp_dir)
+ z.write(absref.value.encode("latin-1"),os.path.basename(absref.value).encode("latin-1"))
+
+ elif (os.path.isfile(tmp_dir + '/' + absref.value)):
+ shutil.copy(tmp_dir + '/' + absref.value,tmp_dir)
+ z.write(tmp_dir + '/' + absref.value.encode("latin-1"),os.path.basename(absref.value).encode("latin-1"))
+
+ xlink.value = os.path.basename(absref.value)
+ absref.value = os.path.basename(absref.value)
+
+
+e = MyEffect()
+e.affect()
diff --git a/share/extensions/svg_dropshadow b/share/extensions/svg_dropshadow
new file mode 100644
index 000000000..0412d4827
--- /dev/null
+++ b/share/extensions/svg_dropshadow
@@ -0,0 +1,88 @@
+#!/usr/bin/perl -w
+#
+# svg_dropshadow
+#
+# Creates drop shadows for all svg elements specified by --id, or
+# whole file if no ids are given.
+#
+# Authors: Daniel Goude (goude@dtek.chalmers.se)
+#
+
+use strict;
+use warnings;
+
+use File::Basename();
+use lib File::Basename::dirname($0);
+
+use SpSVG;
+
+my $sp = new SpSVG;
+
+# Set the script name, used when displaying --help
+$sp->set_name($0);
+
+# Set usage string (options are handled separately).
+my $usage = <<EOF;
+Creates drop shadows from svg group(s)
+EOF
+$sp->set_usage($usage);
+
+# Set script specific options and description (used for --help)
+# SpSVG will hasdle in/out files, and help
+my @opt_vals = (
+ {
+ "opt" => "color=s",
+ "desc" => "Shadow color (default black)",
+ },
+
+
+ {
+ "opt" => "opacity=s",
+ "desc" => "Shadow offset (0-1, default 0.5)",
+ },
+
+ {
+ "opt" => "offset=s",
+ "desc" => "Shadow offset, default 10",
+ },
+);
+
+my %opts = $sp->get_opts(@opt_vals);
+
+my $color = $opts{'color'} || 'black';
+my $opacity = $opts{'opacity'} || '0.5';
+my $offset= $opts{'offset'} || '10';
+
+# Read input file (from --file or STDIN)
+$sp->parse;
+
+# Apply make_shadow to selected ids, or whole file
+$sp->process_ids(\&make_shadow);
+
+# Dump the file (to --output or STDOUT)
+$sp->dump;
+
+# That's it!
+
+# make_shadow takes an svg fragment and returns named fragment
+# with a shadow added
+sub make_shadow {
+ my $element = shift;
+
+ # Duplicate element
+ my $shadow = $element;
+
+ # Set shadow color
+ $shadow =~ s/(stroke|fill):[^;]+;/$1:$color;/ig;
+
+ my $svg = <<EOF;
+ <svg:g id="fooz" style="opacity:$opacity;" transform="translate($offset,
+$offset)">
+ $shadow
+ </svg:g>
+ $element
+EOF
+ return $svg;
+}
+
+
diff --git a/share/extensions/svgz_input.inx b/share/extensions/svgz_input.inx
new file mode 100644
index 000000000..ad142d0c9
--- /dev/null
+++ b/share/extensions/svgz_input.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>SVGZ Input</_name>
+ <id>org.inkscape.input.svgz</id>
+ <dependency type="executable">gzip</dependency>
+ <dependency type="extension">org.inkscape.input.svg</dependency>
+ <input>
+ <extension>.svgz</extension>
+ <mimetype>image/x-svgz</mimetype>
+ <_filetypename>Compressed Inkscape SVG (*.svgz)</_filetypename>
+ <_filetypetooltip>Inkscape's native file format compressed with GZip</_filetypetooltip>
+ <output_extension>org.inkscape.output.svgz</output_extension>
+ </input>
+ <script>
+ <command reldir="path">gzip -cd</command>
+ <check reldir="path">gzip</check>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/svgz_output.inx b/share/extensions/svgz_output.inx
new file mode 100644
index 000000000..0db0e188b
--- /dev/null
+++ b/share/extensions/svgz_output.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>SVGZ Output</_name>
+ <id>org.inkscape.output.SVGZ</id>
+ <dependency type="extension">org.inkscape.output.svg.inkscape</dependency>
+ <dependency type="executable">gzip</dependency>
+ <output>
+ <extension>.svgz</extension>
+ <mimetype>image/x-svgz</mimetype>
+ <_filetypename>Compressed Inkscape SVG (*.svgz)</_filetypename>
+ <_filetypetooltip>Inkscape's native file format compressed with GZip</_filetypetooltip>
+ <dataloss>FALSE</dataloss>
+ </output>
+ <script>
+ <command reldir="path">gzip -c</command>
+ <helper_extension>org.inkscape.output.svg.inkscape</helper_extension>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/txt2svg.inx b/share/extensions/txt2svg.inx
new file mode 100644
index 000000000..4e70d5ce9
--- /dev/null
+++ b/share/extensions/txt2svg.inx
@@ -0,0 +1,16 @@
+<inkscape-extension>
+ <_name>Text Input</_name>
+ <id>org.inkscape.input.txt</id>
+ <dependency type="executable" location="extensions">txt2svg.pl</dependency>
+ <dependency type="executable" location="path">perl</dependency>
+ <input>
+ <extension>.txt</extension>
+ <mimetype>text/html</mimetype>
+ <_filetypename>Text File (*.txt)</_filetypename>
+ <_filetypetooltip>ASCII Text</_filetypetooltip>
+ </input>
+ <script>
+ <command reldir="extensions" interpreter="perl">txt2svg.pl</command>
+ <check reldir="extensions">txt2svg.pl</check>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/txt2svg.pl b/share/extensions/txt2svg.pl
new file mode 100755
index 000000000..ea5769797
--- /dev/null
+++ b/share/extensions/txt2svg.pl
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+
+# This is a script to render a plain text file into SVG, line by line.
+
+use strict;
+use SVG;
+use vars qw($VERSION);
+$VERSION = '1.00';
+
+my $svg = new SVG;
+
+$svg->comment('Generated by txt2svg');
+
+my $i=0;
+while (<>) {
+ chomp($_);
+ s/\t/ /g; # Convert tabs into spaces, otherwise we get errors about invalid char
+
+ my $text = $svg->text(id => "text_line_$i",
+ x => 10,
+ y => 12*(1+$i),
+ 'xml:space' => 'preserve',
+ style => { 'font' => 'Courier',
+ 'font-family' => 'Courier 10 pitch',
+ 'font-size' => 10,
+ }
+ )
+ ->cdata($_);
+ $i++;
+}
+
+print $svg->xmlify();
+
diff --git a/share/extensions/wavy.inx b/share/extensions/wavy.inx
new file mode 100644
index 000000000..64a96d293
--- /dev/null
+++ b/share/extensions/wavy.inx
@@ -0,0 +1,17 @@
+<inkscape-extension>
+ <_name>Function Plotter</_name>
+ <id>org.ekips.filter.wavy</id>
+ <dependency type="executable" location="extensions">wavy.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="periods" type="float" min="0.0" max="1000.0" _gui-text="Periods (2*Pi each)">4.0</param>
+ <param name="samples" type="int" min="1" max="1000" _gui-text="Nodes per period">8</param>
+ <param name="fofx" type="string" _gui-text="Function">sin(x)</param>
+ <param name="fponum" type="boolean" _gui-text="Calculate first derivative numerically">true</param>
+ <param name="fpofx" type="string" _gui-text="First derivative">cos(x)</param>
+ <effect>
+ <object-type>rect</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">wavy.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/wavy.py b/share/extensions/wavy.py
new file mode 100755
index 000000000..aa438e121
--- /dev/null
+++ b/share/extensions/wavy.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+drawwave() was translated into python from the postscript version
+described at http://www.ghostscript.com/person/toby/ and located
+at http://www.telegraphics.com.au/sw/sine.ps .
+http://www.tinaja.com/glib/bezsine.pdf shows another method for
+approximating sine with beziers.
+
+The orginal postscript version displayed the following copyright
+notice and was released under the terms of the GPL:
+Copyright (C) 2001-3 Toby Thain, toby@telegraphics.com.au
+'''
+import inkex, simplepath, simplestyle
+from math import *
+from random import *
+
+def drawwave(samples, periods, width, height, left, top,
+ fx = "sin(x)", fpx = "cos(x)", fponum = True):
+
+ # step is the distance between nodes on x
+ step = 2*pi / samples
+ third = step / 3.0
+
+ # coords and scales based on the source rect
+ xoff = left
+ yoff = top + (height / 2)
+ scalex = width / (2*pi * periods)
+ scaley = height / 2
+ procx = lambda x: x * scalex + xoff
+ procy = lambda y: y * scaley + yoff
+
+ # functions specified by the user
+ if fx != "":
+ f = eval('lambda x: ' + fx)
+ if fpx != "":
+ fp = eval('lambda x: ' + fpx)
+
+ # initialize function and derivative for 0;
+ # they are carried over from one iteration to the next, to avoid extra function calculations
+ y0 = f(0)
+ if fponum == True: # numerical derivative, using 0.001*step as the small differential
+ d0 = (f(0 + 0.001*step) - y0)/(0.001*step)
+ else: # derivative given by the user
+ d0 = fp(0)
+
+ a = [] # path array
+ a.append(['M',[procx(0.0), procy(y0)]]) # initial moveto
+
+ for i in range(int(samples * periods)):
+ x = i * step
+ y1 = f(x + step)
+ if fponum == True: # numerical derivative
+ d1 = (y1 - f(x + step - 0.001*step))/(0.001*step)
+ else: # derivative given by the user
+ d1 = fp(x + step)
+ # create curve
+ a.append(['C',[procx(x + third), procy(y0 + (d0 * third)),
+ procx(x + (step - third)), procy(y1 - (d1 * third)),
+ procx(x + step), procy(y1)]])
+ y0 = y1 # next segment's y0 is this segment's y1
+ d0 = d1 # we assume the function is smooth everywhere, so carry over the derivative too
+
+ return a
+
+class Wavy(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-p", "--periods",
+ action="store", type="float",
+ dest="periods", default=4.0,
+ help="Periods (2*Pi each)")
+ self.OptionParser.add_option("-s", "--samples",
+ action="store", type="int",
+ dest="samples", default=8,
+ help="Samples per period")
+ self.OptionParser.add_option("--fofx",
+ action="store", type="string",
+ dest="fofx", default="sin(x)",
+ help="f(x) for plotting")
+ self.OptionParser.add_option("--fponum",
+ action="store", type="inkbool",
+ dest="fponum", default=True,
+ help="Calculate the first derivative numerically")
+ self.OptionParser.add_option("--fpofx",
+ action="store", type="string",
+ dest="fpofx", default="cos(x)",
+ help="f'(x) for plotting")
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ if node.tagName == 'rect':
+ new = self.document.createElement('svg:path')
+ x = float(node.attributes.getNamedItem('x').value)
+ y = float(node.attributes.getNamedItem('y').value)
+ w = float(node.attributes.getNamedItem('width').value)
+ h = float(node.attributes.getNamedItem('height').value)
+
+ s = node.attributes.getNamedItem('style').value
+ new.setAttribute('style', s)
+ try:
+ t = node.attributes.getNamedItem('transform').value
+ new.setAttribute('transform', t)
+ except AttributeError:
+ pass
+ new.setAttribute('d', simplepath.formatPath(
+ drawwave(self.options.samples,
+ self.options.periods,
+ w,h,x,y,
+ self.options.fofx,
+ self.options.fpofx,
+ self.options.fponum)))
+ node.parentNode.appendChild(new)
+ node.parentNode.removeChild(node)
+
+e = Wavy()
+e.affect()
diff --git a/share/extensions/whirl.inx b/share/extensions/whirl.inx
new file mode 100644
index 000000000..5b7069d33
--- /dev/null
+++ b/share/extensions/whirl.inx
@@ -0,0 +1,16 @@
+<inkscape-extension>
+ <_name>Whirl</_name>
+ <id>org.ekips.filter.whirl</id>
+ <dependency type="executable" location="extensions">whirl.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <param name="centerx" type="float" min="0.0" max="10000.0" _gui-text="Center X">100.0</param>
+ <param name="centery" type="float" min="0.0" max="10000.0" _gui-text="Center Y">100.0</param>
+ <param name="whirl" type="float" min="0.00" max="1000.00" _gui-text="Amount of Whirl">100.0</param>
+ <param name="rotation" type="boolean" _gui-text="Direction of Rotation">true</param>
+ <effect>
+ <object-type>path</object-type>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">whirl.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/whirl.py b/share/extensions/whirl.py
new file mode 100644
index 000000000..35dbc0840
--- /dev/null
+++ b/share/extensions/whirl.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+'''
+Copyright (C) 2005 Aaron Spike, aaron@ekips.org
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+'''
+import math, inkex, cubicsuperpath
+
+class Whirl(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+ self.OptionParser.add_option("-x", "--centerx",
+ action="store", type="float",
+ dest="centerx", default=10.0,
+ help="")
+ self.OptionParser.add_option("-y", "--centery",
+ action="store", type="float",
+ dest="centery", default=0.0,
+ help="")
+ self.OptionParser.add_option("-t", "--whirl",
+ action="store", type="float",
+ dest="whirl", default=1.0,
+ help="amount of whirl")
+ self.OptionParser.add_option("-r", "--rotation",
+ action="store", type="inkbool",
+ dest="rotation", default=True,
+ help="direction of rotation")
+ def effect(self):
+ for id, node in self.selected.iteritems():
+ rotation = 1
+ if self.options.rotation == True:
+ rotation = -1
+ whirl = self.options.whirl / 1000
+ if node.tagName == 'path':
+ d = node.attributes.getNamedItem('d')
+ p = cubicsuperpath.parsePath(d.value)
+ for sub in p:
+ for csp in sub:
+ for point in csp:
+ point[0] -= self.options.centerx
+ point[1] -= self.options.centery
+ dist = math.sqrt((point[0] ** 2) + (point[1] ** 2))
+ if dist != 0:
+ a = rotation * dist * whirl
+ theta = math.atan2(point[1], point[0]) + a
+ point[0] = (dist * math.cos(theta))
+ point[1] = (dist * math.sin(theta))
+ point[0] += self.options.centerx
+ point[1] += self.options.centery
+ d.value = cubicsuperpath.formatPath(p)
+
+e = Whirl()
+e.affect()
diff --git a/share/extensions/wmf_input.inx b/share/extensions/wmf_input.inx
new file mode 100644
index 000000000..99b234896
--- /dev/null
+++ b/share/extensions/wmf_input.inx
@@ -0,0 +1,14 @@
+<inkscape-extension>
+ <_name>Windows Metafile Input</_name>
+ <id>org.inkscape.input.wmf</id>
+ <dependency type="executable">wmf2svg</dependency>
+ <input>
+ <extension>.wmf</extension>
+ <mimetype>application/x-wmf</mimetype>
+ <_filetypename>Windows Metafile (*.wmf)</_filetypename>
+ <_filetypetooltip>A popular graphics file format for clipart</_filetypetooltip>
+ </input>
+ <script>
+ <command reldir="path">wmf2svg</command>
+ </script>
+</inkscape-extension>