diff options
Diffstat (limited to 'share/extensions')
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> |
