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)
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))
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