├── README ├── layer2png.inx └── layer2png.py /README: -------------------------------------------------------------------------------- 1 | Inkscape Layer Slicer 2 | ===================== 3 | 4 | It can be useful to "slice" images and export the slices. This 5 | extension facilitates that. 6 | 7 | Here's the process I've used for slicing web layout with 8 | Inkscape. Create your webpage layout (set page units to "px", 9 | width/height appropriately and snap to 1 pixel intervals. This should 10 | allow pixel perfect alignment). Then create a new layer, naming it 11 | slices. Draw rectangles over the areas you want to slice (set 12 | x,y,width,height to whole pixel values). Name these rectangles using 13 | the Object Properties found in the right click contextual menu (the 14 | saved images name will be based on that value, so name them something 15 | like "header" instead of the default/non-useful "rect4312"). 16 | 17 | What the plugin then does is iterate over all of the rectangle 18 | definitions found in the slice layer and set the opacity of the slice 19 | rectangle to 0. (That allows you to make slightly transparent slices, 20 | which are easier to deal with than invisible ones) It then creates 21 | pngs for every slice. After completing the slicing, it then sets all 22 | the slice to a different color at 25% opacity. 23 | 24 | * red - overwrote a file 25 | * green - wrote a new file 26 | * grey - skipped (not overwriting) 27 | 28 | To continue working on your design without having to deal with the 29 | slices being selected, just "lock" the slice layer and your clicks 30 | should fall through. You can also click the "eye" on the slice layer 31 | to not view it. 32 | 33 | LICENSE 34 | ======= 35 | 36 | GPL2 37 | 38 | Installation 39 | ============ 40 | 41 | Put the .inx file and .py file in ~/.config/inkscape/extensions/ and restart 42 | inkscape. 43 | 44 | For global installation place in /usr/share/inkscape/extensions (on 45 | gentoo, your distro may vary) 46 | 47 | For MS place in \Inkscape\share\extensions\ 48 | 49 | Status 50 | ====== 51 | 52 | I've used this since 2007 for simple web graphics and doing 53 | presentation images. It was submitted to Inkscape for inclusion but 54 | rejected because it clobbers existing exported files. 55 | 56 | See https://bugs.launchpad.net/inkscape/+bug/169985 57 | 58 | Note that it now includes an option to overwrite or not existing 59 | files. 60 | 61 | NEWS 62 | ====== 63 | 64 | * Oct 2019 - Attempting to get into Inkscape proper again. See https://gitlab.com/inkscape/extensions/merge_requests/139 65 | -------------------------------------------------------------------------------- /layer2png.inx: -------------------------------------------------------------------------------- 1 | 2 | 3 | <_name>Export Layer Slices 4 | org.inkscape.output.layer2png 5 | org.inkscape.output.svg.inkscape 6 | layer2png.py 7 | inkex.py 8 | 12 | 13 | 14 | 15 | 300 16 | false 17 | 128, 64, 48, 32, 24, 16 18 | slices 19 | true 20 | 21 | 22 | <_param name="slicerhelp" type="description">This effect slices images, which can be useful for webdesign. In order to use it, create a new layer on top of the image. In this layer create rectaingles that define the slices. 23 | Note: 24 | * Slices may overlap. 25 | * It is also useful to set Document Properties units to "px" and use whole integer sizes for rectangles and snap to grid. 26 | * The slice ID ("Id" field of "Object Properties" right click contextual menu) of the rectangle is used to determine the filename (the suffix of .png is added). 27 | * When invoked any color settings on the layer rectangles is ignored and the images below them are exported to the directory choosen. 28 | * If overwrite is not checked, then if an existing file with that name exists, the export will not happen. 29 | * After export all the rectangles in the export layer are set to different color (red-overwrote a file, green-wrote a new file, grey-skipped (not overwritting)). 30 | * It is useful to hide and lock the export layer while doing work so it is not distracting. 31 | 32 | 33 | 34 | 35 | 37 | 38 | all 39 | 40 | 41 | 42 | 43 | 44 | 47 | 48 | -------------------------------------------------------------------------------- /layer2png.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | layer2png.py 4 | 5 | Copyright (C) 2007-2010 Matt Harrison, matthewharrison [at] gmail.com 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | 22 | 23 | A script that slices images. It might be useful for web design. 24 | 25 | You pass it the name of a layer containing rectangles that cover 26 | the areas that you want exported (the default name for this layer 27 | is "slices"). It then sets the opacity to 0 for all the rectangles 28 | defined in that layer and exports as png whatever they covered. 29 | The output filenames are based on the "Id" field of "Object Properties" 30 | right click contextual menu of the rectangles. 31 | 32 | One side effect is that after exporting, it sets the slice rectangles 33 | to different colors with a 25% opacity. (If you want to hide them, 34 | just click on the eye next to the layer). 35 | 36 | * red - overwrote a file 37 | * green - wrote a new file 38 | * grey - skipped (not overwriting) 39 | 40 | For good pixel exports set the Document Properties, default units to "px" 41 | and the width/height to the real size. (I use 1024x768) 42 | 43 | """ 44 | import os 45 | import sys 46 | import logging 47 | import tempfile 48 | 49 | try: 50 | from subprocess import Popen, PIPE 51 | bsubprocess = True 52 | except: 53 | bsubprocess = False 54 | 55 | try: 56 | import xml.etree.ElementTree as et 57 | except ImportError as e: 58 | try: 59 | from lxml import etree as et 60 | except: 61 | sys.exit(_('The fantastic lxml wrapper for libxml2 is required by inkex.py and therefore this extension. Please download and install the latest version from http://cheeseshop.python.org/pypi/lxml/, or install it through your package manager by a command like: sudo apt-get install python-lxml')) 62 | 63 | sys.path.append('/usr/share/inkscape/extensions') 64 | 65 | import inkex 66 | import simplestyle 67 | 68 | logging.basicConfig(filename=os.path.join(tempfile.gettempdir(), 'inklog.log'), level=logging.DEBUG) 69 | 70 | class ExportSlices(inkex.Effect): 71 | """Exports all rectangles in the current layer""" 72 | def __init__(self): 73 | inkex.Effect.__init__(self) 74 | self.color_map = {} # change color based on overwrite 75 | # green - new export 76 | # red - overwrite 77 | # grey - not exported (no overwrite) 78 | self.OptionParser.add_option("--tab", 79 | action="store", type="string", 80 | dest="tab", default="sampling", 81 | help="The selected UI-tab when OK was pressed") 82 | self.OptionParser.add_option("-d", "--directory", 83 | action="store", type="string", 84 | dest="directory", default=os.path.expanduser("~"), 85 | help="Existing destination directory") 86 | self.OptionParser.add_option("-l", "--layer", 87 | action="store", type="string", 88 | dest="layer_name", default="slices", 89 | help="Layer with slices (rects) in it") 90 | self.OptionParser.add_option("-i", "--iconmode", 91 | action="store", type="inkbool", default=False, 92 | help="Icon export mode") 93 | self.OptionParser.add_option("-s", "--sizes", 94 | action="store", type="string", 95 | dest="sizes", default="128, 64, 48, 32, 24, 16", 96 | help="sizes to export comma separated") 97 | self.OptionParser.add_option("-o", "--overwrite", 98 | action="store", type="inkbool", default=False, 99 | help="Overwrite existing exports?") 100 | self.OptionParser.add_option("--dpi", 101 | action="store", type="string", 102 | dest="dpi", default="300", 103 | help="Dots per inch (300 default)") 104 | 105 | 106 | def effect(self): 107 | """ 108 | In addition to the command line parameters a (temp) file 109 | containing the contents of the svg is passed on the command 110 | line (self.args[-1]) 111 | """ 112 | if not os.path.isdir(self.options.directory): 113 | os.makedirs(self.options.directory) 114 | 115 | if not self.layer_exists(self.options.layer_name): 116 | sys.stderr.write("Export layer: '%s' does not exist. Please see 'Help' tab for instructions" % self.options.layer_name) 117 | logging.log(logging.DEBUG, "No slice layer: %s" % self.options.layer_name) 118 | return 119 | 120 | logging.log(logging.DEBUG, "COMMAND LINE %s" % sys.argv) 121 | # set opacity to zero in slices 122 | slices_found = False 123 | for node in self.get_layer_nodes(self.document, self.options.layer_name): 124 | slices_found = True 125 | self.clear_color(node) 126 | 127 | if not slices_found: 128 | sys.stderr.write("No rectangles defined in '%s' to slice. Please see 'Help' tab for instructions" % self.options.layer_name) 129 | logging.log(logging.DEBUG, "No rectangles defined in '%s' to slice" % self.options.layer_name) 130 | return 131 | 132 | # save new xml 133 | fout = open(self.args[-1], 'w') 134 | self.document.write(fout) 135 | fout.close() 136 | 137 | # in case there are overlapping rects, clear them all out before 138 | # saving any 139 | for node in self.get_layer_nodes(self.document, self.options.layer_name): 140 | if self.options.iconmode: 141 | for s in self.options.sizes.split(', '): 142 | if s.isdigit(): 143 | pngSize = int(s) 144 | self.export_resized(node, pngSize, pngSize) 145 | else: 146 | self.export_original_size(node) 147 | 148 | #change slice colors to grey/green/red and set opacity to 25% in real document 149 | for node in self.get_layer_nodes(self.document, self.options.layer_name): 150 | self.change_color(node) 151 | 152 | def layer_exists(self, layer_name): 153 | layer_nodes = self.get_layer_nodes(self.document, layer_name) 154 | return layer_nodes != None 155 | 156 | def get_layer_nodes(self, document, layer_name): 157 | """ 158 | given an xml document (etree), and the name of a layer one 159 | that contains the rectangles defining slices, return the nodes 160 | of the rectangles. 161 | """ 162 | #get layer we intend to slice 163 | slice_node = None 164 | slice_layer = document.findall('{http://www.w3.org/2000/svg}g') 165 | for node in slice_layer: 166 | label_value = node.attrib.get('{http://www.inkscape.org/namespaces/inkscape}label', None) 167 | if label_value == layer_name: 168 | slice_node = node 169 | 170 | if slice_node is not None: 171 | return slice_node.findall('{http://www.w3.org/2000/svg}rect') 172 | return slice_node 173 | 174 | def clear_color(self, node): 175 | ''' 176 | set opacity to zero, and stroke to none 177 | 178 | Node looks like this: 179 |