Code

Extensions. Compressed+media export improvements (see Bug #386664, Gather Resources...
[inkscape.git] / share / extensions / svg_and_media_zip_output.py
1 #!/usr/bin/env python
2 '''
3 svg_and_media_zip_output.py
4 An extention which collects all images to the documents directory and
5 creates a zip archive containing all images and the document
7 Copyright (C) 2005 Pim Snel, pim@lingewoud.com
8 Copyright (C) 2008 Aaron Spike, aaron@ekips.org
9 Copyright (C) 2011 Nicolas Dufour, nicoduf@yahoo.fr
10     * Fix for a bug related to special caracters in the path (LP #456248).
11     * Fix for Windows support (LP #391307 ).
12     * Font list and image directory features.
14 this is  the first Python script  ever created
15 its based on embedimage.py
17 This program is free software; you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation; either version 2 of the License, or
20 (at your option) any later version.
22 This program is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
31 TODOs
32 - fix bug: not saving existing .zip after a Collect for Output is run
33      this bug occurs because after running an effect extention the inkscape:output_extension is reset to svg.inkscape
34      the file name is still xxx.zip. after saving again the file xxx.zip is written with a plain .svg which
35      looks like a corrupt zip
36 - maybe add better extention
37 - consider switching to lzma in order to allow cross platform compression with no encoding problem...
38 '''
40 import inkex
41 import urlparse
42 import urllib
43 import os, os.path
44 import string
45 import zipfile
46 import shutil
47 import sys
48 import tempfile
49 import simplestyle
50 import gettext
51 import locale
52 locale.setlocale(locale.LC_ALL, '')
53 _ = gettext.gettext
55 class CompressedMediaOutput(inkex.Effect):
56     def __init__(self):
57         inkex.Effect.__init__(self)
58         if os.name == 'nt':
59             self.encoding = "cp437"
60         else:
61             self.encoding = "latin-1"
62         self.text_tags = ['{http://www.w3.org/2000/svg}tspan',
63                             '{http://www.w3.org/2000/svg}text',
64                             '{http://www.w3.org/2000/svg}flowRoot',
65                             '{http://www.w3.org/2000/svg}flowPara',
66                             '{http://www.w3.org/2000/svg}flowSpan']
67         self.OptionParser.add_option("--image_dir",
68                                      action="store", type="string",
69                                      dest="image_dir", default="",
70                                      help="Image directory")
71         self.OptionParser.add_option("--font_list",
72                                      action="store", type="inkbool",
73                                      dest="font_list", default=False,
74                                      help="Add font list")
75         self.OptionParser.add_option("--tab",
76                                      action="store", type="string",
77                                      dest="tab",
78                                      help="The selected UI-tab when OK was pressed")
80     def output(self):
81         '''
82         Writes the temporary compressed file to its destination
83         and removes the temporary directory.
84         '''
85         out = open(self.zip_file,'rb')
86         if os.name == 'nt':
87             try:
88                 import msvcrt
89                 msvcrt.setmode(1, os.O_BINARY)
90             except:
91                 pass
92         sys.stdout.write(out.read())
93         out.close()
94         shutil.rmtree(self.tmp_dir)
96     def collect_images(self, docname, z):
97         '''
98         Collects all images in the document
99         and copy them to the temporary directory.
100         '''
101         if locale.getpreferredencoding():
102           dir_locale = locale.getpreferredencoding()
103         else:
104           dir_locale = "UTF-8"
105         dir = unicode(self.options.image_dir, dir_locale)
106         for node in self.document.xpath('//svg:image', namespaces=inkex.NSS):
107             xlink = node.get(inkex.addNS('href',u'xlink'))
108             if (xlink[:4] != 'data'):
109                 absref = node.get(inkex.addNS('absref',u'sodipodi'))
110                 url = urlparse.urlparse(xlink)
111                 href = urllib.url2pathname(url.path)
112                 
113                 if (href != None):
114                     absref = os.path.realpath(href)
116                 absref = unicode(absref, "utf-8")
117                 image_path = os.path.join(dir, os.path.basename(absref))
118                 
119                 if (os.path.isfile(absref)):
120                     shutil.copy(absref, self.tmp_dir)
121                     z.write(absref, image_path.encode(self.encoding))
122                 elif (os.path.isfile(os.path.join(self.tmp_dir, absref))):
123                     # TODO: please explain why this clause is necessary
124                     shutil.copy(os.path.join(self.tmp_dir, absref), self.tmp_dir)
125                     z.write(os.path.join(self.tmp_dir, absref), image_path.encode(self.encoding))
126                 else:
127                     inkex.errormsg(_('Could not locate file: %s') % absref)
129                 node.set(inkex.addNS('href',u'xlink'), image_path)
130                 #node.set(inkex.addNS('absref',u'sodipodi'), image_path)
132     def collect_SVG(self, docstripped, z):
133         '''
134         Copy SVG document to the temporary directory
135         and add it to the temporary compressed file
136         '''
137         dst_file = os.path.join(self.tmp_dir, docstripped)
138         stream = open(dst_file,'w')
139         self.document.write(stream)
140         stream.close()
141         z.write(dst_file,docstripped.encode(self.encoding)+'.svg')
143     def is_text(self, node):
144         '''
145         Returns true if the tag in question is an element that
146         can hold text.
147         '''
148         return node.tag in self.text_tags
150     def get_fonts(self, node):
151         '''
152         Given a node, returns a list containing all the fonts that
153         the node is using.
154         '''
155         fonts = []
156         font_familly = ''
157         font_weight = ''
158         s = ''
159         if 'style' in node.attrib:
160             s = simplestyle.parseStyle(node.attrib['style'])
161         if not s:
162             return fonts
163         if s['font-weight']:
164             font_weight = s['font-weight']
165         if s['font-family']:
166             font_familly = s['font-family']
167         fonts.append(font_familly + ' ' + font_weight)
168         return fonts
170     def list_fonts(self, z):
171         '''
172         Walks through nodes, building a list of all fonts found, then
173         reports to the user with that list.
174         Based on Craig Marshall's replace_font.py
175         '''
176         items = []
177         nodes = []
178         items = self.document.getroot().getiterator()
179         nodes.extend(filter(self.is_text, items))
180         fonts_found = []
181         for node in nodes:
182             for f in self.get_fonts(node):
183                 if not f in fonts_found:
184                     fonts_found.append(f)
185         findings = sorted(fonts_found)
186         # Write list to the temporary compressed file
187         filename = 'fontlist.txt'
188         dst_file = os.path.join(self.tmp_dir, filename)
189         stream = open(dst_file,'w')
190         if len(findings) == 0:
191             stream.write(_("Didn't find any fonts in this document/selection."))
192         else:
193             if len(findings) == 1:
194                 stream.write(_("Found the following font only: %s") % findings[0])
195             else:
196                 stream.write(_("Found the following fonts:\n%s") % '\n'.join(findings))
197         stream.close()
198         z.write(dst_file, filename)
201     def effect(self):
202         docroot = self.document.getroot()
203         docname = docroot.get(inkex.addNS('docname',u'sodipodi'))
204         #inkex.errormsg(_('Locale: %s') % locale.getpreferredencoding())
205         if docname is None:
206             docname = self.args[-1]
207         # TODO: replace whatever extention
208         docstripped = os.path.basename(docname.replace('.zip', ''))
209         docstripped = docstripped.replace('.svg', '')
210         docstripped = docstripped.replace('.svgz', '')
211         # Create os temp dir
212         self.tmp_dir = tempfile.mkdtemp()
213         # Create destination zip in same directory as the document
214         self.zip_file = os.path.join(self.tmp_dir, docstripped) + '.zip'
215         z = zipfile.ZipFile(self.zip_file, 'w')
217         self.collect_images(docname, z)
218         self.collect_SVG(docstripped, z)
219         if self.options.font_list == True:
220             self.list_fonts(z)
221         z.close()
224 if __name__ == '__main__':   #pragma: no cover
225     e = CompressedMediaOutput()
226     e.affect()
229 # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 fileencoding=utf-8 textwidth=99