使用 ZopfliPNG 自动优化 CSS 文件(数据 URI)中的 PNG

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

我有一些 CSS 和带有内联图像(base64 编码数据 URI [AKA 数据 URL])的 LESS 文件,其中一些是 PNG。我希望这些 PNG 以自动方式使用 ZopfliPNG 进行编码。

遗憾的是,ZopfliPNG 似乎只能处理文件而不是例如文件。使用 stdin/stdout(像“Support piping input”这样的错误跟踪器条目仍然打开)这让事情变得有点复杂。

Python基于解决方案:

#!/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 @ Whosebug (https://whosebug.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)

在输入和输出文件应该不同的假设下工作(根据我的测试,ZopfliPNG 也可以工作,输入和输出与您对这个程序的期望相同,但可以肯定的是,参数也是如此显然似乎迫使人们使用不同的输出文件)。有一点微优化,同时它重用了它创建的临时文件。

然后在包含 CSS/LESS/... 文件的文件夹上使用它,例如在 UNIX shell 中你可以这样做:

find /css-folder/ -type f -name '*.*ss' -exec \
  /path/to/this_script_here.py /path/to/zopfli/zopflipng {} +

Leanify(我写的)支持优化 CSS 文件中的数据 URI 图像。它在内部对 PNG 文件使用 ZopfliPNG。

它比您的 python 脚本有一些优势:

  1. 它还支持其他图像格式,如 JPEG、ICO 和 SVG。

  2. 不需要临时文件,一切都在内存中完成。

  3. Data URI搜索兼容性更好。对于CSS文件,url(data:image之间可能有'",它还支持在HTML和JS文件中搜索,这可能根本没有url(

https://github.com/JayXon/Leanify