使用 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 脚本有一些优势:
它还支持其他图像格式,如 JPEG、ICO 和 SVG。
不需要临时文件,一切都在内存中完成。
Data URI搜索兼容性更好。对于CSS文件,url(
和data:image
之间可能有'
或"
,它还支持在HTML和JS文件中搜索,这可能根本没有url(
。
我有一些 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 脚本有一些优势:
它还支持其他图像格式,如 JPEG、ICO 和 SVG。
不需要临时文件,一切都在内存中完成。
Data URI搜索兼容性更好。对于CSS文件,
url(
和data:image
之间可能有'
或"
,它还支持在HTML和JS文件中搜索,这可能根本没有url(
。