Automated optimization of PNGs inside CSS files (data URIs) using ZopfliPNG

I have some CSS and LESS files with inline images (base64 encoded data URIs [AKA data URLs]) of which some are PNGs. I want these PNGs to be encoded using ZopfliPNGs in an automated fashion.

Sadly it seems like ZopfliPNG can only deal with with files and not eg with stdin/stdout (bug tracker entries like "Support piping input" are still open) which makes things a bit more complicated.


Leanify (I wrote) supports optimizing Data URI images in CSS files. It's using ZopfliPNG internally for PNG files.

It has a few advantages over your python script:

  • It also support other image format such as JPEG, ICO and SVG.

  • It does not need temporary files, everything is done in memory.

  • The Data URI searching has better compatibility. For CSS files, there might be ' or " between url( and data:image , it also support searching in HTML and JS files which might not have url( at all.

  • https://github.com/JayXon/Leanify


    Python-based solution:

    #!/usr/bin/env python
    
    ''' Tool to optimize PNG images embedded as data URLs in CSS files or similar
    using ZopfliPNG.
    '''
    
    import base64
    import re
    import subprocess
    import sys
    import tempfile
    
    __author__ = "phk @ stackoverflow (https://stackoverflow.com/users/2261442)"
    __version__ = "1.0.1"
    
    # parameters for ZopfliPNG controlling the optimization method
    OPTIMIZATION_PARAMS = [
        "-m",
        "--lossy_transparent",
        "--iterations=1000"
    ]
    
    if len(sys.argv) < 2:
        print("Usage: {} ZOPFLIPNG_EXECUTABLE TARGET_FILES...".format(sys.argv[0]))
        sys.exit(1)
    
    zopflipng = sys.argv[1]
    targets = sys.argv[2:]
    
    # regex to match all the data urls with PNGs inside CSS or therelike
    # but only return the base64 encoded PNG data
    inline_png_re = re.compile(
        r"(?<=url(data:image/png;base64,)[A-Za-z0-9+/]+=*(?=))")
    
    # create temporary input/output files for ZopfliPNG, it only deals with files
    with tempfile.NamedTemporaryFile('w+b') as tmpf_in, 
            tempfile.NamedTemporaryFile('r+b') as tmpf_out:
    
        def replace_inline_png(match):
            ''' Replace all the PNGs inside data URLs with optimized versions. '''
    
            orig_data = match.group(0)
    
            try:
                data = base64.b64decode(orig_data)
            except TypeError:
                print("Invalid base64 string. Skipping this data URL.")
                return orig_data
    
            # prepare input file for ZopfliPNG
            tmpf_in.seek(0)  # because the temporary input file gets re-used
            tmpf_in.truncate()
            tmpf_in.write(data)
            tmpf_in.flush()  # because the file is kept open
    
            tmpf_out.seek(0)
            tmpf_out.truncate()
    
            return_code = subprocess.call([
                zopflipng,
                "-y",  # silent overwriting of output file necessary
            ] + OPTIMIZATION_PARAMS + [
                tmpf_in.name,
                tmpf_out.name
            ])
    
            if return_code:
                print("ZopfliPNG reported an error. Skipping this PNG.")
                return orig_data
    
            # read zopflipng results from output file
            data = tmpf_out.read()
    
            return base64.b64encode(data)
    
        def optimize_file(target):
            ''' Optimize the PNGs embedded as data URLs in target file. '''
    
            try:
                with open(target) as f_in:
                    contents = f_in.read()
            except IOError:
                print("Can't open {} for reading!".format(target))
                return
    
            # replace the inline PNGs with optimized versions
            contents = inline_png_re.sub(replace_inline_png, contents)
    
            try:
                # write the changed file contents
                with open(target, 'w') as f_out:
                    f_out.write(contents)
            except IOError:
                print("Can't open {} for writing!".format(target))
                return
    
        for target in targets:
            optimize_file(target)
    

    Works under the assumption that the input and output files should be different (from my tests ZopfliPNG would also work it input and output were the same as you would expect from this of program but just to be sure, also the parameters apparently seemed to force one to use a different output file). Has a little micro-optimization whereas it reuses the temporary files it creates.

    To use it then on a folder with CSS/LESS/… files eg in a UNIX shell you could do:

    find /css-folder/ -type f -name '*.*ss' -exec 
      /path/to/this_script_here.py /path/to/zopfli/zopflipng {} +
    
    链接地址: http://www.djcxy.com/p/31854.html

    上一篇: 大数据从arules包转换为“交易”

    下一篇: 使用ZopfliPNG自动优化CSS文件(数据URI)内的PNG