Code

Extensions. New guillotine extension (testing).
authorJazzyNico <nicoduf@yahoo.fr>
Wed, 25 Aug 2010 04:43:37 +0000 (06:43 +0200)
committerJazzyNico <nicoduf@yahoo.fr>
Wed, 25 Aug 2010 04:43:37 +0000 (06:43 +0200)
po/POTFILES.in
share/extensions/Makefile.am
share/extensions/guillotine.inx [new file with mode: 0644]
share/extensions/guillotine.py [new file with mode: 0644]

index 0fdec6a4ebf804f2f549d9ee59bc4aaa2f5d3cae..ab7222d176da36fd37df1aa93a4764721f3feb3a 100644 (file)
@@ -58,6 +58,7 @@
 [type: gettext/xml] share/extensions/grid_cartesian.inx
 [type: gettext/xml] share/extensions/grid_polar.inx
 [type: gettext/xml] share/extensions/guides_creator.inx
+[type: gettext/xml] share/extensions/guillotine.inx
 [type: gettext/xml] share/extensions/handles.inx
 [type: gettext/xml] share/extensions/hpgl_output.inx
 [type: gettext/xml] share/extensions/inkscape_help_askaquestion.inx
@@ -510,6 +511,7 @@ share/extensions/web-transmit-att.py
 [type: gettext/xml] share/extensions/grid_cartesian.inx
 [type: gettext/xml] share/extensions/grid_polar.inx
 [type: gettext/xml] share/extensions/guides_creator.inx
+[type: gettext/xml] share/extensions/guillotine.inx
 [type: gettext/xml] share/extensions/handles.inx
 [type: gettext/xml] share/extensions/hpgl_output.inx
 [type: gettext/xml] share/extensions/inkscape_help_askaquestion.inx
index 78532f9391bea59440ba55833a1d72598a1331ac..36a25d56f84b0d6add117390aa370409a632d38e 100644 (file)
@@ -67,6 +67,7 @@ extensions = \
        grid_cartesian.py \
        grid_polar.py \
        guides_creator.py \
+    guillotine.py \
        handles.py \
        hpgl_output.py \
        ill2svg.pl \
@@ -221,6 +222,7 @@ modules = \
        grid_cartesian.inx \
        grid_polar.inx \
        guides_creator.inx \
+    guillotine.inx \
        handles.inx \
        hpgl_output.inx \
        inkscape_help_askaquestion.inx \
diff --git a/share/extensions/guillotine.inx b/share/extensions/guillotine.inx
new file mode 100644 (file)
index 0000000..ba88f66
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">\r
+    <_name>Guillotine</_name>\r
+    <id>org.inkscape.guillotine</id>\r
+    \r
+       <dependency type="extension">org.inkscape.output.svg.inkscape</dependency>\r
+       \r
+       <dependency type="executable" location="extensions">guillotine.py</dependency>\r
+       <dependency type="executable" location="extensions">inkex.py</dependency>\r
+       \r
+       <param name="directory" type="string" _gui-text="Directory to save images to">~/</param>\r
+       <param name="image" type="string" _gui-text="Image name (without extension)">guillotined</param>\r
+       <param name="ignore" type="boolean" _gui-text="Ignore these settings and use export hints?">false</param>\r
+       \r
+    <effect needs-live-preview="false">\r
+                <object-type>all</object-type>\r
+                <effects-menu>\r
+                      <submenu _name="Export"/>\r
+                </effects-menu>\r
+    </effect>\r
+    \r
+    <script>\r
+        <command reldir="extensions" interpreter="python">guillotine.py</command>\r
+    </script>\r
+    \r
+</inkscape-extension>\r
diff --git a/share/extensions/guillotine.py b/share/extensions/guillotine.py
new file mode 100644 (file)
index 0000000..7c43daa
--- /dev/null
@@ -0,0 +1,248 @@
+#!/usr/bin/env python \r
+'''\r
+guillotine.py\r
+\r
+Copyright (C) 2010 Craig Marshall, craig9 [at] gmail.com\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+This program is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with this program; if not, write to the Free Software\r
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA\r
+\r
+-----------------------\r
+\r
+This script slices an inkscape drawing along the guides, similarly to \r
+the GIMP plugin called "guillotine". It can optionally export to the \r
+same directory as the SVG file with the same name, but with a number\r
+suffix. e.g. \r
+\r
+/home/foo/drawing.svg\r
+\r
+will export to:\r
+\r
+/home/foo/drawing0.png\r
+/home/foo/drawing1.png\r
+/home/foo/drawing2.png\r
+/home/foo/drawing3.png\r
+\r
+etc.\r
+\r
+'''\r
+\r
+import os\r
+import sys\r
+import inkex\r
+import simplestyle\r
+import locale\r
+\r
+locale.setlocale(locale.LC_ALL, '')\r
+\r
+try:\r
+    from subprocess import Popen, PIPE\r
+    bsubprocess = True\r
+except:\r
+    bsubprocess = False\r
+\r
+def float_sort(a, b):\r
+    '''\r
+    This is used to sort the horizontal and vertical guide positions,\r
+    which are floating point numbers, but which are held as text.\r
+    '''\r
+    return cmp(float(a), float(b))\r
+\r
+class Guillotine(inkex.Effect):\r
+    """Exports slices made using guides"""\r
+    def __init__(self):\r
+        inkex.Effect.__init__(self)    \r
+        self.OptionParser.add_option("--directory", action="store", \r
+                                        type="string", dest="directory",\r
+                                        default=None, help="")\r
+                                        \r
+        self.OptionParser.add_option("--image", action="store", \r
+                                        type="string", dest="image", \r
+                                        default=None, help="")\r
+                                        \r
+        self.OptionParser.add_option("--ignore", action="store", \r
+                                        type="inkbool", dest="ignore", \r
+                                        default=None, help="")\r
+        \r
+    def get_guides(self):\r
+        '''\r
+        Returns all guide elements as an iterable collection\r
+        '''\r
+        root = self.document.getroot()\r
+        guides = []\r
+        xpath = self.document.xpath("//sodipodi:guide", \r
+                                    namespaces=inkex.NSS)\r
+        for g in xpath:\r
+            guide = {}\r
+            (x, y) = g.attrib['position'].split(',')\r
+            if g.attrib['orientation'] == '0,1':\r
+                guide['orientation'] = 'horizontal'\r
+                guide['position'] = y\r
+                guides.append(guide)\r
+            elif g.attrib['orientation'] == '1,0':\r
+                guide['orientation'] = 'vertical'\r
+                guide['position'] = x\r
+                guides.append(guide)\r
+        return guides\r
+    \r
+    def get_all_horizontal_guides(self):\r
+        '''\r
+        Returns all horizontal guides as a list of floats stored as \r
+        strings. Each value is the position from 0 in pixels.\r
+        '''\r
+        guides = []\r
+        for g in self.get_guides():\r
+            if g['orientation'] == 'horizontal':\r
+                guides.append(g['position'])\r
+        return guides\r
+\r
+    def get_all_vertical_guides(self):\r
+        '''\r
+        Returns all vertical guides as a list of floats stored as\r
+        strings. Each value is the position from 0 in pixels.\r
+        '''\r
+        guides = []\r
+        for g in self.get_guides():\r
+            if g['orientation'] == 'vertical':\r
+                guides.append(g['position'])\r
+        return guides        \r
+        \r
+    def get_horizontal_slice_positions(self):\r
+        '''\r
+        Make a sorted list of all horizontal guide positions, \r
+        including 0 and the document height, but not including\r
+        those outside of the canvas\r
+        '''\r
+        root = self.document.getroot()\r
+        horizontals = ['0']\r
+        height = inkex.unittouu(root.attrib['height'])\r
+        for h in self.get_all_horizontal_guides():\r
+            if h >= 0 and float(h) <= float(height):\r
+                horizontals.append(h)\r
+        horizontals.append(height)\r
+        horizontals.sort(cmp=float_sort)    \r
+        return horizontals\r
+        \r
+    def get_vertical_slice_positions(self):\r
+        '''\r
+        Make a sorted list of all vertical guide positions,\r
+        including 0 and the document width, but not including\r
+        those outside of the canvas. \r
+        '''\r
+        root = self.document.getroot()\r
+        verticals = ['0']\r
+        width = inkex.unittouu(root.attrib['width'])\r
+        for v in self.get_all_vertical_guides():\r
+            if v >= 0 and float(v) <= float(width):\r
+                verticals.append(v)\r
+        verticals.append(width)\r
+        verticals.sort(cmp=float_sort)\r
+        return verticals\r
+    \r
+    def get_slices(self):\r
+        '''\r
+        Returns a list of all "slices" as denoted by the guides\r
+        on the page. Each slice is really just a 4 element list of \r
+        floats (stored as strings), consisting of the X and Y start \r
+        position and the X and Y end position.\r
+        '''\r
+        hs = self.get_horizontal_slice_positions()\r
+        vs = self.get_vertical_slice_positions()\r
+        slices = []\r
+        for i in range(len(hs)-1):\r
+            for j in range(len(vs)-1):\r
+                slices.append([vs[j], hs[i], vs[j+1], hs[i+1]])\r
+        return slices\r
+        \r
+    def get_filename_parts(self):\r
+        '''\r
+        Attempts to get directory and image as passed in by the inkscape \r
+        dialog. If the boolean ignore flag is set, then it will ignore\r
+        these settings and try to use the settings from the export\r
+        filename.\r
+        '''\r
+        \r
+        if self.options.ignore == False:\r
+            return (self.options.directory, self.options.image)\r
+        else:\r
+            '''\r
+            First get the export-filename from the document, if the \r
+            document has been exported before (TODO: Will not work if it\r
+            hasn't been exported yet), then uses this to return a tuple \r
+            consisting of the directory to export to, and the filename \r
+            without extension.\r
+            '''\r
+            svg = self.document.getroot()\r
+            att = '{http://www.inkscape.org/namespaces/inkscape}export-filename'\r
+            try:\r
+                export_file = svg.attrib[att]\r
+            except KeyError:\r
+                inkex.errormsg("To use the export hints option, you " +\r
+                "need to have previously exported the document. " + \r
+                "Otherwise no export hints exist!")\r
+                sys.exit(-1)\r
+            dirname, filename = os.path.split(export_file)\r
+            filename = filename.rsplit(".", 1)[0] # Without extension\r
+            return (dirname, filename)    \r
+\r
+    def check_dir_exists(self, dir):\r
+        if not os.path.isdir(dir):\r
+            os.makedirs(dir)\r
+\r
+    def get_localised_string(self, str):\r
+        return locale.format("%.f", float(str), 0)\r
+\r
+    def export_slice(self, s, filename):\r
+        '''\r
+        Runs inkscape's command line interface and exports the image \r
+        slice from the 4 coordinates in s, and saves as the filename \r
+        given.\r
+        '''\r
+        svg_file = self.args[-1]\r
+        command = "inkscape -a %s:%s:%s:%s -e \"%s\" \"%s\" " % (self.get_localised_string(s[0]), self.get_localised_string(s[1]), self.get_localised_string(s[2]), self.get_localised_string(s[3]), filename, svg_file)\r
+        if bsubprocess:\r
+            p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)\r
+            return_code = p.wait()\r
+            f = p.stdout\r
+            err = p.stderr\r
+        else:\r
+            _, f, err = os.open3(command)\r
+        f.close()\r
+        \r
+    def export_slices(self, slices):\r
+        '''\r
+        Takes the slices list and passes each one with a calculated \r
+        filename/directory into export_slice.\r
+        '''\r
+        dirname, filename = self.get_filename_parts()\r
+        if dirname == '' or dirname == None:\r
+            dirname = './'\r
+        inkex.errormsg(dirname)\r
+        dirname = os.path.expanduser(dirname)\r
+        dirname = os.path.expandvars(dirname)\r
+        self.check_dir_exists(dirname)\r
+        i = 0\r
+        for s in slices:\r
+            f = dirname + os.path.sep + filename + str(i) + ".png"\r
+            self.export_slice(s, f)\r
+            i += 1\r
+    \r
+    def effect(self):\r
+        slices = self.get_slices()\r
+        self.export_slices(slices)\r
+    \r
+if __name__ == "__main__":\r
+    e = Guillotine()\r
+    e.affect()\r
+\r