summaryrefslogtreecommitdiffstats
path: root/share/extensions/layer2png.py
blob: 9b28d31f9b649b374c87effe02f7d1d1e6f00052 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env python
# coding=utf-8
#
# Copyright (C) 2007-2019 Matt Harrison, matthewharrison [at] gmail.com
# 
# 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
# 
"""
A script that slices images.  It might be useful for web design.

You pass it the name of a layer containing rectangles that cover
the areas that you want exported (the default name for this layer
is "slices").  It then sets the opacity to 0 for all the rectangles
defined in that layer and exports as png whatever they covered.
The output filenames are based on the "Id" field of "Object Properties"
right click contextual menu of the rectangles.

One side effect is that after exporting, it sets the slice rectangles
to different colors with a 25% opacity.  (If you want to hide them,
just click on the eye next to the layer).

  * red - overwrote a file
  * green - wrote a new file
  * grey - skipped (not overwriting)

For good pixel exports set the Document Properties, default units to "px"
and the width/height to the real size. (I use 1024x768)

Here's the process I've used for slicing web layout with
Inkscape: Create your webpage layout (set page units to "px",
width/height appropriately and snap to 1 pixel intervals. This should
allow pixel perfect alignment). Then create a new layer, naming it
slices. Draw rectangles over the areas you want to slice (set
x,y,width,height to whole pixel values). Name these rectangles using
the Object Properties found in the right click contextual menu (the
saved images name will be based on that value, so name them something
like "header" instead of the default/non-useful "rect4312").
"""
import os
import tempfile

import inkex
from inkex.command import inkscape

class ExportSlices(inkex.EffectExtension):
    """Exports all rectangles in the current layer"""
    GREEN = "#00ff00"  # new export
    GREY = "#555555"   # not exported
    RED = "#ff0000"    # overwrite


    def __init__(self):
        super(ExportSlices, self).__init__()
        self.color_map = {}  # map node id to color based on overwrite


    def add_arguments(self, pars):
        pars.add_argument("--tab")
        pars.add_argument("--directory", default=os.path.expanduser("~"),\
            help="Existing destination directory")
        pars.add_argument("--layer", default="slices", help="Layer with slices (rects) in it")
        pars.add_argument("--iconmode", type=inkex.Boolean, help="Icon export mode")
        pars.add_argument("--sizes", default="128, 64, 48, 32, 24, 16",\
            help="sizes to export comma separated")
        pars.add_argument("--overwrite", type=inkex.Boolean, help="Overwrite existing exports?")
        pars.add_argument("--dpi", default="300", help="Dots per inch (300 default)")

    def effect(self):
        if not os.path.isdir(self.options.directory):
            os.makedirs(self.options.directory)

        nodes = self.get_layer_nodes(self.options.layer)
        if nodes is None:
            raise inkex.AbortExtension("Slice: '{}' does not exist.".format(self.options.layer))

        # set opacity to zero in slices
        for node in nodes:
            self.clear_color(node)

        # save file once now
        # if we have multiple slices we will make multiple calls
        # to inkscape
        (_, tmp_svg) = tempfile.mkstemp('.svg')
        with open(tmp_svg, 'wb') as fout:
            fout.write(self.svg.tostring())

        # in case there are overlapping rects, clear them all out before
        # saving any
        for node in nodes:
            if self.options.iconmode:
                for size in self.options.sizes.split(","):
                    size = size.strip()
                    if size.isdigit():
                        png_size = int(size)
                        self.export_node(node, png_size, png_size)
            else:
                self.export_node(node)

        # change slice colors to grey/green/red and set opacity to 25% in real document
        for node in nodes:
            self.change_color(node)
        return self.document

    def get_layer_nodes(self, layer_name):
        """
        given the name of a layer one that contains the rectangles defining slices,
        return the nodes of the rectangles.
        """
        # get layer we intend to slice
        slice_node = None
        slice_layer = self.svg.findall('svg:g')
        for node in slice_layer:
            label_value = node.label 
            if label_value == layer_name:
                slice_node = node

        if slice_node is not None:
            return slice_node.findall('svg:rect')
        return slice_node


    def clear_color(self, node):
        """
        set opacity to zero, and stroke to none

        Node looks like this:
        <rect
        style="opacity:0;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:4.00099993;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;display:inline"
        """
        node.style.update({"stroke": "none", "opacity": "0"})

    def change_color(self, node):
        """
        set color from color_map and set opacity to 25%
        
        """
        node_id = node.attrib["id"]
        color = self.color_map[node_id]
        node.style.update({"fill": color, "opacity": ".25"})

    def export_node(self, node, height=None, width=None):
        color, kwargs = self.get_color_and_command_kwargs(node, height, width)
        node_id = node.attrib["id"]
        self.color_map[node_id] = color
        if color == ExportSlices.GREY:  # skipping
            return
        svg_file = self.options.input_file 
        inkscape(svg_file, **kwargs)

    def get_color_and_command_kwargs(self, node, height=None, width=None):
        directory = self.options.directory
        node_id = node.attrib['id']
        size = '' if height is None else '-{}x{}'.format(width, height)
        file_name = "{}{}.png".format(node_id, size)
        filename = os.path.join(directory, file_name)
        color = ExportSlices.GREY  # skipping
        if self.options.overwrite or not os.path.exists(filename):
            color = ExportSlices.RED  #  overwritten
            if not os.path.exists(filename):
                color = ExportSlices.GREEN  # new export
            kwargs = {'export-id': node_id, 'export-filename': filename,
                      'export-dpi': self.options.dpi}
            if width:
                kwargs['export-height'] = str(height)
                kwargs['export-width'] = str(width)
            return color, kwargs
        else:
            inkex.errormsg("Export exists ({}) not overwriting".format(filename))
            return color, {}

if __name__ == "__main__":
    ExportSlices().run()