def test_should_prefix_style_url_references(self): src = self.get_test_image_path('test_svg_with_style_url_ref.svg') dst = os.path.join(tempfile.gettempdir(), 'resized_svg_image.svg') try: resize_svg_image(src, dst, 0, 0, crop=False, optimize=True, prefix='123') svg = file_get_contents(dst) self.assertEqual( '<?xml version="1.0" encoding="utf-8"?>\n' + '<svg data-prefix="b23" id="b23_Layer_1" style="enable-background:new 0 0 64 64;" version="1.1" viewBox="0 0 64 64" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n\n' + '<linearGradient gradientUnits="userSpaceOnUse" id="b23_SVGID_1_" x1="0" x2="64" y1="32.3013" y2="32.3013">\n' + '<stop offset="0" style="stop-color:#B90404"/>\n' + '<stop offset="1" style="stop-color:#8E0000"/>\n' + '</linearGradient>\n' + '<path d="36.3,83.7 0.1,62.7 0,20.8 36.1,0 72.2,21.1 72.4,62.9" style="fill:url(#b23_SVGID_1_);"/>\n' + '</svg>', svg) finally: if os.path.isfile(dst): os.unlink(dst)
def test_should_enforce_prefix_always_starting_with_letter(self): src = self.get_test_image_path('test_svg_with_ids.svg') dst = os.path.join(tempfile.gettempdir(), 'resized_svg_image.svg') try: resize_svg_image(src, dst, 0, 0, crop=False, optimize=True, prefix='foo') svg = file_get_contents(dst) self.assertEqual( '<?xml version="1.0" encoding="utf-8"?>\n' + \ '<svg data-prefix="foo" id="foo_Layer_1" style="enable-background:new 0 0 164.4 83.7;" version="1.1" viewBox="0 0 164.4 83.7" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n' + \ '<polygon id="foo_base" points="36.3,83.7 0.1,62.7 0,20.8 36.1,0 72.2,21.1 72.4,62.9 "/>\n' + \ '<polygon id="foo_cuffs" points="36.4,62.9 54.5,73 54.5,52.4 "/>\n' + \ '<g id="foo_test" transform="matrix(0.250391,0,0,0.250391,36.1,46.1023)">\n' + \ '<use height="512px" width="512px" x="0" xlink:href="#foo_Image1" y="0"/>\n' + \ '</g>\n' + \ '<defs>\n' + \ '<image height="512px" id="foo_Image1" width="512px" xlink:href="data:image/png;base64,broken image"/>\n' + \ '</defs>\n' + \ '</svg>', svg ) finally: if os.path.isfile(dst): os.unlink(dst)
def merge_files(filenames, base, filetype, dst_filename, identifier=None): """ Merge multiple files together and return their content. """ content = [] for fname in filenames: filename = fname if filetype == 'js': filename = compile_if_require_js_hook(filename) # silently ignore missing files try: txt = file_get_contents(filename) except IOError: txt = '' # Replace any django template variables by running it through a template # and return the template content if 'templating' in filename: txt = serve_static_with_context(txt, identifier) if len(txt) > 0: if filetype == 'css': txt = relocate_css_relative_resource_urls(txt, base, filename, dst_filename) content.append(txt) content.append('\n') return '\n'.join(content)
def _assertStaticFile(self, filename, contains_content=None): path = os.path.join(settings.STATIC_ROOT, filename) self.assertTrue(os.path.isfile(path), 'expected file to exist: %s' % path) if contains_content: self.assertIn(contains_content, file_get_contents(path))
def test_invalidate_should_rename_cached_files_and_clear_cache(self): # build cache self.cache.add('foo.html', None, None, '<h1>Foo</h1>', minify_html=False) self.cache.add('bar.html', None, None, '<h1>Bar</h1>', minify_html=False) self.cache.add('test/index.html', None, None, '<h1>Test</h1>', minify_html=False) self.cache.write() # invalidate self.cache.invalidate() # original files are gone self.assertFalse( os.path.isfile(os.path.join(settings.CACHE_ROOT, 'foo.html'))) self.assertFalse( os.path.isfile(os.path.join(settings.CACHE_ROOT, 'bar.html'))) self.assertFalse( os.path.isfile( os.path.join(settings.CACHE_ROOT, 'test', 'index.html'))) # files renamed (. prefix) self.assertEqual( '<h1>Foo</h1>', file_get_contents(os.path.join(settings.CACHE_ROOT, '.foo.html'))) self.assertEqual( '<h1>Bar</h1>', file_get_contents(os.path.join(settings.CACHE_ROOT, '.bar.html'))) self.assertEqual( '<h1>Test</h1>', file_get_contents( os.path.join(settings.CACHE_ROOT, 'test', '.index.html'))) # index file removed self.assertFalse( os.path.isfile( os.path.join(settings.CACHE_ROOT, self.cache.index_filename)))
def test_should_save_timestamp_in_iso_format(self): ts = save_deploy_timestamp() timestamp = datetime.strptime( file_get_contents(get_deploy_timestamp_filename()), '%Y-%m-%dT%H:%M:%S' ) self.assertEqual(datetime.now(), timestamp) self.assertEqual(datetime.now(), ts)
def load_resource_version_identifier(): """ Load resource version identifier from the website's deployment target folder. """ filename = os.path.join(settings.STATIC_ROOT, 'revision') try: return file_get_contents(filename) except IOError: return None
def test_should_overwrite_existing_file(self): file_put_contents(get_deploy_timestamp_filename(), 'hello world') save_deploy_timestamp() timestamp = datetime.strptime( file_get_contents(get_deploy_timestamp_filename()), '%Y-%m-%dT%H:%M:%S' ) self.assertEqual(datetime.now(), timestamp)
def get_pid(cls): """ Return the process identifier (pid) that is stored in the pid file for the task runner. Returns None if no pid file exists or the pid cannot be parsed as an integer number. """ try: return int(file_get_contents(cls.get_pid_filename())) except (ValueError, IOError): return None
def test_create_svgicons_management_command_should_generate_svgicon_file(self): from cubane.svgicons.management.commands.create_svgicons import Command self.call_command(Command()) # - email and location are stripped (style) # - phone style has been retained (see resource declaration in testapp) self.assertEqual( '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display: none;"><symbol id="svgicon-email" viewBox="0 0 64 64"><g id="email-sc8O5C.tif"> <g> <path d="M6.4,13.7c17.1,0,34.1,0,51.2,0c0.1,0.1,0.2,0.1,0.4,0.2c1.3,0.8,1.4,2,0.4,3c-5.5,5.5-11,11-16.4,16.5 c-2.7,2.7-5.4,5.5-8.1,8.2c-1,1-2.6,1.1-3.5,0.1c-2-2-3.9-3.9-5.9-5.9c-3.5-3.5-6.9-7-10.4-10.5c-2.9-3-5.9-5.9-8.8-8.9 c-0.3-0.3-0.4-0.6-0.3-1C5.1,14.6,5.6,14,6.4,13.7z"/> <path d="M32,52.3c-8.1,0-16.2,0-24.4,0c-1,0-1.8-0.2-2.4-1.1c-0.5-0.9-0.5-1.4,0.3-2.1c4.5-4.6,9.1-9.1,13.6-13.7 c0.5-0.5,0.5-0.5,1,0c2.8,2.8,5.5,5.5,8.3,8.3c2,2,5.2,2,7.2,0c2.8-2.8,5.5-5.6,8.3-8.3c0.5-0.5,0.5-0.5,1,0 c2.9,2.9,5.7,5.8,8.6,8.6c1.7,1.7,3.4,3.5,5.2,5.2c0.4,0.4,0.5,0.9,0.4,1.4c-0.2,1-1.1,1.6-2.1,1.7c-0.2,0-0.4,0-0.6,0 C48.2,52.3,40.1,52.3,32,52.3z"/> <path d="M4.9,32.9c0-4,0-8,0-12.1c0-0.2-0.1-0.5,0.1-0.6c0.2-0.1,0.3,0.2,0.5,0.4c3.9,4,7.9,7.9,11.8,11.9c0.4,0.4,0.3,0.6,0,0.9 c-4,4-7.9,8-11.9,12c-0.1,0.1-0.2,0.3-0.4,0.2c-0.2-0.1-0.1-0.3-0.1-0.5C4.9,41,4.9,37,4.9,32.9z"/> <path d="M59.1,32.9c0,3.9,0,7.9,0,11.8c0,0.1,0,0.3,0,0.4c0,0.1,0.1,0.3-0.1,0.4c-0.2,0.1-0.3-0.1-0.4-0.2 c-0.7-0.7-1.4-1.4-2.1-2.1c-1.8-1.8-3.6-3.6-5.4-5.4c-1.5-1.5-3-3-4.5-4.5c-0.3-0.3-0.3-0.5,0-0.9c4-4,7.9-8,11.9-11.9 c0.1-0.1,0.2-0.2,0.3-0.2c0.1-0.1,0.3-0.1,0.3,0.1c0,0.2,0,0.3,0,0.5C59.1,24.9,59.1,28.9,59.1,32.9z"/> </g> </g></symbol><symbol id="svgicon-location" viewBox="0 0 64 64"><g id="location-xNv91c.tif"> <g> <path d="M28.4,0c1.8,0,3.5,0,5.3,0c0.1,0,0.2,0.1,0.4,0.1c8.7,1.3,15.1,5.8,18.9,13.7c2.1,4.2,2.9,8.8,1.9,13.5 c-0.7,3.4-2.2,6.5-3.9,9.6c-4.2,7.7-9.4,14.7-15,21.5c-1.5,1.9-3.1,3.7-4.7,5.6c-0.1,0-0.2,0-0.3,0C24.8,57,19,49.7,14,41.7 c-3.1-4.9-5.9-10-7-15.8c0-1.5,0-3,0-4.5c0-0.2,0.1-0.4,0.1-0.6c1.5-9.1,6.4-15.4,14.8-19.1C24,0.8,26.2,0.4,28.4,0z"/> </g> </g></symbol><symbol id="svgicon-phone" viewBox="0 0 60 60"><path d="M9.8,6.2c-0.9,0.6-7.2,5.2-8.1,10.4S0.5,27.1,6.2,36.2c5.9,9.3,17.4,20.7,27.4,21.3c8.9,0.5,15.2-5.7,15.9-8.1 c0.7-2.3-2.7-5.8-3.6-6.5c-0.8-0.6-5.6-4.9-8.4-4.3c-4.6,0.9-4.3,4.9-8.4,5.3c-2.4,0.2-5.3-1.5-9.6-6.8c-9.4-11.7-4.8-12.2-3.1-13.7 c1.7-1.5,4.7-3.2,4.1-5.6s-3-8.5-5.5-10.6c-1.8-1.7-2.1-1.6-2.8-1.7S10.7,5.6,9.8,6.2z" style="fill:#FFFFFF;"/></symbol></svg>', file_get_contents(self._get_path()) )
def test_should_save_version_to_file(self): version = generate_resource_version_identifier() save_resource_version_identifier(version) filename = get_resource_version_filename() self.assertTrue(os.path.isfile(filename)) content = file_get_contents(filename) os.remove(filename) self.assertEqual(version, content)
def load_deploy_timestamp(): """ Load resource version identifier from the website's deployment target folder. """ try: return datetime.strptime( file_get_contents(get_deploy_timestamp_filename()), TIMESTAMP_FORMAT) except (IOError, ValueError): return None
def test_should_write_file_content_as_unicode(self): tmp = tempfile.gettempdir() filename = os.path.join(tmp, 'test.txt') content = unicode('Hello World') file_put_contents(filename, content) content_read = file_get_contents(filename) self.assertEqual(content_read, content) self.assertIsInstance(content_read, unicode) os.remove(filename)
def test_should_receive_file_content_as_unicode(self): tmp = tempfile.gettempdir() filename = os.path.join(tmp, 'test.txt') content = unicode('Hello World') with codecs.open(filename, 'w', encoding='utf-8') as f: f.write(content) content_read = file_get_contents(filename) self.assertEqual(content_read, content) self.assertIsInstance(content_read, unicode) os.remove(filename)
def _copy_file(src_path, dst_path, context): """ Copy the given file to the given dest. path. The filename and it's content may be substituted. """ if is_text_file(src_path): # text, substritute content against given context content = file_get_contents(src_path) content = _get_substituted_content(content, context) file_put_contents(dst_path, content) else: # binary, directly copy without substitution shutil.copyfile(src_path, dst_path)
def test_should_generate_src_files_if_feature_is_turned_on(self): base_path = self.get_testapp_static_path() dst_filename = os.path.join(tempfile.gettempdir(), 'minified.css') src_filename = os.path.join(tempfile.gettempdir(), 'minified.css.src') minify_files([os.path.join(base_path, 'css', 'test_minify.css')], base_path, dst_filename, 'css') content = file_get_contents(src_filename) os.remove(dst_filename) os.remove(src_filename) self.assertEqual('\nbody {\n color: red;\n}\n\n', content)
def test_add_file_to_cache_should_remove_invalidated_content_version_if_exists( self): # build cache lastmod = datetime(2016, 6, 18, 16, 55, 23) self.cache.add('index.html', lastmod, None, '<h1>Foo</h1>', minify_html=False) self.cache.write() # invalidate self.cache.invalidate() self.assertFalse( os.path.isfile(os.path.join(settings.CACHE_ROOT, 'index.html'))) self.assertEqual( '<h1>Foo</h1>', file_get_contents(os.path.join(settings.CACHE_ROOT, '.index.html'))) # add updated content with a newer timestamp again (changed) new_lastmod = datetime(2016, 6, 18, 17, 55, 23) self.cache = Cache() self.cache.add('index.html', new_lastmod, None, '<h1>Foo</h1>', minify_html=False) self.cache.write() # invalidated version should have been removed self.assertFalse( os.path.isfile(os.path.join(settings.CACHE_ROOT, '.index.html'))) self.assertEqual( '<h1>Foo</h1>', file_get_contents(os.path.join(settings.CACHE_ROOT, 'index.html'))) self.assertEqual(new_lastmod, self._get_cache_mtime('index.html'))
def test_should_crop_to_new_aspect_ratio_based_on_center_position_for_portrait(self): src = self.get_test_image_path('test_svg_crop_portrait.svg') dst = os.path.join(tempfile.gettempdir(), 'resized_svg_image.svg') try: resize_svg_image(src, dst, 400, 400, prefix='') svg = file_get_contents(dst) self.assertEqual( '<?xml version="1.0" encoding="utf-8"?>\n' + \ '<svg id="Layer_1" style="enable-background:new 0 40 80 80;" version="1.1" viewBox="0 40 80 80" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n' + \ '<polygon fill="red" points="36.3,83.7 0.1,62.7 0,20.8 36.1,0 72.2,21.1 72.4,62.9 "/>\n' + \ '</svg>', svg ) finally: if os.path.isfile(dst): os.unlink(dst)
def handle(self, *args, **options): """ Run command. """ # load source data filename = os.path.join(settings.CUBANE_PATH, 'rawdata', 'countries.json') json = decode_json(file_get_contents(filename)) # generate xml file content xml = [] xml.append('<?xml version="1.0" encoding="utf-8"?>') xml.append('<django-objects version="1.0">') for c in json: # country name(s) name = c.get('name') # num code num_code = c.get('ccn3') # calling code calling_code = c.get('callingCode') if len(calling_code) >= 1: calling_code = calling_code[0] else: calling_code = None # generate xml xml.append(' <object pk="%s" model="cubane.country">' % c.get('cca2')) xml.append(' <field type="CharField" name="name">%s</field>' % name.get('common').upper()) xml.append(' <field type="BooleanField" name="flag_state">0</field>') xml.append(' <field type="CharField" name="printable_name">%s</field>' % name.get('common')) xml.append(' <field type="CharField" name="iso3">%s</field>' % c.get('cca3')) xml.append(' <field type="BooleanField" name="landlocked">%s</field>' % ('1' if c.get('landlocked') else '0')) if num_code: xml.append(' <field type="PositiveSmallIntegerField" name="numcode">%s</field>' % num_code) xml.append(' <field type="CharField" name="calling_code">%s</field>' % calling_code) xml.append(' </object>') xml.append('</django-objects>') # save to xml file (fixture) filename = os.path.join(settings.CUBANE_PATH, 'fixtures', 'cubane', 'country.xml') file_put_contents(filename, '\n'.join(xml)) # import fixture file call_command('loaddata', 'cubane/country.xml', interactive=False)
def _get_minified_content(self, filenames, filetype): """ Minify given list of files of given file type. """ if not isinstance(filenames, list): filenames = [filenames] dst_filename = os.path.join(tempfile.gettempdir(), 'minified') base_path = self.get_testapp_static_path() filenames = [ os.path.join(base_path, filename) for filename in filenames ] minify_files(filenames, base_path, dst_filename, filetype) content = file_get_contents(dst_filename) os.remove(dst_filename) return content
def get_resource(url_path): """ Return the actual content of the file given by url_path, which is the absolute web path. """ path = get_resource_path(url_path) # fail in DEBUG, otherwise return empty content in production if settings.DEBUG: if not os.path.isfile(path): raise ValueError( 'Unable to load resource content from file \'%s\'.' % path ) try: return file_get_contents(path) except IOError: return ''
def test_should_inline_style(self): src = self.get_test_image_path('test_svg_with_style.svg') dst = os.path.join(tempfile.gettempdir(), 'resized_svg_image.svg') try: resize_svg_image(src, dst, 164.4, 83.7, optimize=True, prefix='') svg = file_get_contents(dst) self.assertEqual( '<?xml version="1.0" encoding="utf-8"?>\n' + \ '<svg id="Layer_1" style="enable-background:new 0 0 164 84;" version="1.1" viewBox="0 0 164 84" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n' + \ '\n' + \ '<polygon points="36.3,83.7 0.1,62.7 0,20.8 36.1,0 72.2,21.1 72.4,62.9 " style="fill:#E8318A;"/>\n' + \ '<polygon points="36.4,62.9 54.5,73 54.5,52.4 " style="fill:#BF2174;"/>\n' + \ '<polygon points="36.4,62.9 36.7,42.1 54.5,52.4 " style="opacity:0.56;fill:#EA53A2;"/>\n' + \ '</svg>', svg ) finally: if os.path.isfile(dst): os.unlink(dst)
def test_should_prefix_clip_path_url_references(self): src = self.get_test_image_path('test_svg_with_clippath_url_ref.svg') dst = os.path.join(tempfile.gettempdir(), 'resized_svg_image.svg') try: resize_svg_image(src, dst, 0, 0, crop=False, optimize=True, prefix='123') svg = file_get_contents(dst) self.assertEqual( '<?xml version="1.0" encoding="utf-8"?>\n' + '<svg data-prefix="b23" id="b23_Layer_1" style="enable-background:new 0 0 64 64;" version="1.1" viewBox="0 0 64 64" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">\n' + '<clipPath id="b23_clip"><path d="M441.105,324.213l44.837,10.308l13.761,-59.851l-44.837,-10.309l-13.761,59.852l0,0Z"/></clipPath>\n' + '<g clip-path="url(#b23_clip)">\n' + '<path d="36.3,83.7 0.1,62.7 0,20.8 36.1,0 72.2,21.1 72.4,62.9"/>\n' + '</g>\n' + '</svg>', svg ) finally: if os.path.isfile(dst): os.unlink(dst)
def _assert_downsampling(self, filename): src = self.get_test_image_path(filename) dst = os.path.join(tempfile.gettempdir(), 'resized_svg_image.svg') try: resize_svg_image(src, dst, 50, 50) # svg file with bitmap data embedded should NOT be the same # image after resize... self.assertFalse(is_same_file(src, dst)) # image size should be less self.assertTrue(os.path.getsize(dst) < os.path.getsize(src)) # the resulting image should still be an svg image with path # information in it content = file_get_contents(dst) self.assertIn('<svg', content) self.assertIn('<path', content) finally: if os.path.isfile(dst): os.unlink(dst)
def inline_svgicons(target): """ Renders inline markup for defining an SVG icon sheet for all SVG icon assets defined for the given bucket name (target). SVG assets are using the standard resource system that is also used for CSS and Javascript assets in combination with resources and inline_resources template tags. """ if target not in get_resource_target_definition(): raise AttributeError( 'Expected valid \'target\' argument for template ' + 'tag \'inline_svgicons\'.') if settings.DEBUG: return mark_safe( get_combined_svg(get_resources(target, 'svg'), get_resources(target, 'svg', 'with-style'))) else: identifier = load_resource_version_identifier() filename = get_svgicons_filename(target, identifier) path = os.path.join(settings.STATIC_ROOT, filename) return mark_safe(file_get_contents(path))
def get_status(cls): """ Return task runner status information. """ try: taskinfo = decode_json(file_get_contents( cls.get_status_filename())) # calc. percentage if taskinfo.get('stopped', False): percent = 0 else: counter = taskinfo.get('recordCounter', 0) total = taskinfo.get('totalRecords', 0) if counter > total: total = counter if total == 0: total = 1 percent = int(round(float(counter) / float(total) * 100.0)) taskinfo['percent'] = percent return taskinfo except IOError: return {}
def resize_svg_image(filename, dst_filename, width, height, focal_point=None, optimize=True, crop=True, prefix=None): """ Scale the given vector image (SVG) to the given width and height and save the result as a new vector image as the given dest. filename. Vector images, such as SVG do not necessarily need to be resized, however they may contain bitmap data which we will scale accordingly. Any vector data and viewport information is preserved. In addition, we will prefix all id attribute values and internal references with a short prefix that is unique to the file. We do this in case we inline multiple SVG files into the actual websites and we do not want to have any identifiers and/or references colliding. Finally, we also remove any style from the SVG, since this may then become global to the document when we inline an SVG. """ def _scale_image(data, width, height): """ Scale given image data blog to fit the given width and height. """ # open image data try: img = WandImage(blob=data) except NOT_AN_IMAGE_WAND_EXCEPTIONS: # not an image! -> ignore and leave as is return None # do not upscale! w, h = img.size if width > w or height > h: width = w height = h if width < 1: width = 1 if height < 1: height = 1 # calc. new width and height by fitting it into the # desired boundaries w, h = get_image_fitting(w, h, width, height) img.resize(w, h) # return image blob blob = io.BytesIO() img.strip() img.save(file=blob) return blob.getvalue() def _match_image(m): """ Process SVG <image> tag. Only deal with base64 embedded image data and ignore external references. """ # extract attributes attr = {} for attrname, value in re.findall( r'(?P<attrname>\w+)="(?P<value>.*?)"', m.group('attr')): attr[attrname] = value # get image data data = attr.get('href', '') href = re.match(r'^data:image\/(?P<fmt>\w+);base64,(?P<data>.*?)$', data) data = None fmt = None if href: fmt = href.group('fmt') try: data = base64.b64decode(href.group('data')) except TypeError: # base64 decoding error! -> ignore and leave as is pass # scale embedded image down to the max. given width, hegiht is # determined by aspect ratio of the image... if data is not None: data = _scale_image(data, width, height) if data is not None: data = base64.b64encode(data) attr['href'] = 'data:image/%s;base64,%s' % (fmt, data) return '<image %s/>' % ' '.join([ '%s="%s"' % (name, value) for name, value in attr.items() ]) # fallback -> leave as is return m.group(0) def _replace_images(svg): """ Find any replace any references to images. """ return re.sub(r'<image (?P<attr>.*?)(?P<tail>(/>)|(></image>))', _match_image, svg) def _get_viewbox(xml): """ Return the viewbox coordinates from the viewBox attribute on the svg tag. """ # parse vieewBox attribute viewbox = xml.svg.get('viewBox', '') components = re.split(r'[\s,]', viewbox, 4) x, y, w, h = (0.0, 0.0, 64.0, 64.0) if len(components) == 4: try: x, y, w, h = [float(c) for c in components] except: pass # do not allow an invalid viewbox, default to square if h == 0.0: h = 1.0 return x, y, w, h def _set_viewbox(xml, x, y, w, h): """ Set the viewBox within the given SVG xml markup to the new dimensions as given. """ # set viewBox xml.svg['viewBox'] = '%s %s %s %s' % (x, y, w, h) # rewrite enable-background style (not really used by many browsders, # but we want to be consistent) style = xml.svg.get('style', '') style = re.sub( r'enable-background:\s*new\s+([-.,\d]+)\s+([-.,\d]+)\s+([-.,\d]+)\s+([-.,\d]+);?', 'enable-background:new %s %s %s %s;' % (x, y, w, h), style) xml.svg['style'] = style def _normalised(x): """ Return integer of x, if x has no fraction component. """ intx = int(x) return intx if intx == x else x def _scale_viewbox(xml, width, height): """ Scale the viewBox to the new aspect ratio of the given width and height. """ # get viewBox from current SVG x, y, w, h = _get_viewbox(xml) # make the target width the same with as the SVG. The target height # is adjusted based on the target aspect ratio if height == 0: height = 1 ar = float(width) / float(height) width = w height = width / ar # determine new cropping area cropx, cropy, cropw, croph = get_image_crop_area( w, h, width, height, focal_point) # adjust to new (cropped) viewBox cropx += x cropy += y # normalise values to int if we do not have a fraction cropx = _normalised(cropx) cropy = _normalised(cropy) cropw = _normalised(cropw) croph = _normalised(croph) # apply new viewbox _set_viewbox(xml, cropx, cropy, cropw, croph) def _inline_style(xml): """ Take embedded style and inline all style information. """ # extract style style = '' for tag in xml.svg.find_all('style'): style += '\n'.join(tag.contents) + '\n' tag.decompose() # inline all collected style inline_style(xml.svg, parse_style(style)) remove_attr(xml.svg, ['class']) def _get_prefix(filename, prefix): """ Return a unique prefix to be used, if the given prefix is None. Since the prefix may be used as unique identifiers, they must start with a letter and cannot start with a number. """ # generate hash based on filename if prefix is None: m = hashlib.sha224() m.update(filename) prefix = m.hexdigest()[:6] # if the first character is a number, it becomes a letter # between a-f depending on the number. if prefix: try: n = int(prefix[0]) prefix = ['a', 'b', 'c', 'd', 'e', 'f'][n % 6] + prefix[1:] except ValueError: pass if prefix != '' and not prefix.endswith('_'): prefix += '_' return prefix def _prefix_ids(node, prefix): prefix_ids(node, prefix) # open svg file as text and replace embedded bitmap data markup = file_get_contents(filename) markup = _replace_images(markup) # load xml if optimize or crop: xml = BeautifulSoup(markup, 'xml') else: xml = None # is SVG? if xml and xml.svg: # crop if crop: _scale_viewbox(xml, width, height) if optimize: _inline_style(xml) _prefix = _get_prefix(dst_filename, prefix) _prefix_ids(xml, _prefix) if _prefix: xml.svg['data-prefix'] = _prefix[:-1] # render xml out if xml: markup = unicode(xml) # write output to target filename file_put_contents(dst_filename, markup)