feat: add comprehensive GitHub workflow and development tools

This commit is contained in:
Stiftung Development
2025-09-06 18:31:54 +02:00
commit ab23d7187e
10224 changed files with 2075210 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
__COMPRESSOR_DOCSTRING__ = """
Args:
data: A string to compress
verbose: (int 0/1) dump zopfli debugging data to stderr
numiterations: Maximum amount of times to rerun forward and backward
pass to optimize LZ77 compression cost. Good values: 10, 15 for
small files, 5 for files over several MB in size or it will be too
slow.
blocksplitting: If true, splits the data in multiple deflate blocks
with optimal choice for the block boundaries. Block splitting gives
better compression. Default: true (1).
blocksplittinglast: If true, chooses the optimal block split points
only after doing the iterative LZ77 compression. If false, chooses
the block split points first, then does iterative LZ77 on each
individual block. Depending on the file, either first or last gives
the best compression. Default: false (0).
blocksplittingmax: Maximum amount of blocks to split into (0 for
unlimited, but this can give extreme results that hurt compression
on some files). Default value: 15.
"""
try:
from ._version import version as __version__ # type: ignore
except ImportError:
__version__ = "0.0.0+unknown"

View File

@@ -0,0 +1,16 @@
# file generated by setuptools_scm
# don't change, don't track in version control
TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import Tuple, Union
VERSION_TUPLE = Tuple[Union[int, str], ...]
else:
VERSION_TUPLE = object
version: str
__version__: str
__version_tuple__: VERSION_TUPLE
version_tuple: VERSION_TUPLE
__version__ = version = '0.2.3.post1'
__version_tuple__ = version_tuple = (0, 2, 3)

View File

@@ -0,0 +1,12 @@
import zopfli
import zopfli.zopfli
def compress(data, *args, **kwargs):
"""gzip.compress(data, **kwargs)
""" + zopfli.__COMPRESSOR_DOCSTRING__ + """
Returns:
String containing a gzip container
"""
kwargs['gzip_mode'] = 1
return zopfli.zopfli.compress(data, *args, **kwargs)

View File

@@ -0,0 +1,152 @@
import zopfli
from zopfli.zopfli import png_optimize as optimize
__all__ = ["optimize"]
def main(args=None):
import argparse
import os
parser = argparse.ArgumentParser(prog="python -m zopfli.png")
parser.add_argument("infile")
parser.add_argument("outfile")
parser.add_argument("-v", "--verbose", action="store_true", help="print more info")
parser.add_argument(
"-m",
action="store_true",
dest="compress_more",
help="compress more: use more iterations (depending on file size).",
)
parser.add_argument(
"-y",
dest="overwrite",
action="store_true",
help="do not ask about overwriting files.",
)
parser.add_argument(
"--lossy_transparent",
action="store_true",
help="remove colors behind alpha channel 0. No visual difference.",
)
parser.add_argument(
"--lossy_8bit",
action="store_true",
help="convert 16-bit per channel image to 8-bit per channel.",
)
parser.add_argument(
"--always_zopflify",
action="store_true",
help="always output the image encoded by Zopfli, even if bigger than original.",
)
parser.add_argument(
"-q",
dest="use_zopfli",
action="store_false",
help="use quick, but not very good, compression.",
)
parser.add_argument(
"--iterations",
default=None,
type=int,
help=(
"number of iterations, more iterations makes it slower but provides "
"slightly better compression. Default: 15 for small files, 5 for large files."
),
)
parser.add_argument(
"--filters",
dest="filter_strategies",
help=(
"filter strategies to try: "
"0-4: give all scanlines PNG filter type 0-4; "
"m: minimum sum; "
"e: entropy; "
"p: predefined (keep from input, this likely overlaps another strategy); "
"b: brute force (experimental). "
"By default, if this argument is not given, one that is most likely the best "
"for this image is chosen by trying faster compression with each type. "
"If this argument is used, all given filter types are tried with slow "
"compression and the best result retained. "
"A good set of filters to try is --filters=0me."
),
)
parser.add_argument(
"--keepchunks",
type=lambda s: s.split(","),
help=(
"keep metadata chunks with these names that would normally be removed, "
"e.g. tEXt,zTXt,iTXt,gAMA, ... Due to adding extra data, this increases "
"the result size. Keeping bKGD or sBIT chunks may cause additional worse "
"compression due to forcing a certain color type, it is advised to not "
"keep these for web images because web browsers do not use these chunks. "
"By default ZopfliPNG only keeps (and losslessly modifies) the following "
"chunks because they are essential: IHDR, PLTE, tRNS, IDAT and IEND."
),
)
options = parser.parse_args(args)
log = print if options.verbose else lambda *_: None
if options.iterations is not None:
num_iterations = num_iterations_large = options.iterations
else:
# these constants are taken from zopflipng_lib.cc, unlikely to ever change
num_iterations, num_iterations_large = 15, 5
if options.compress_more:
num_iterations *= 4
num_iterations_large *= 4
with open(options.infile, "rb") as f:
input_png = f.read()
log(f"Optimizing {options.infile}")
result_png = optimize(
input_png,
verbose=options.verbose,
lossy_transparent=options.lossy_transparent,
lossy_8bit=options.lossy_8bit,
filter_strategies=options.filter_strategies,
keepchunks=options.keepchunks,
use_zopfli=options.use_zopfli,
num_iterations=num_iterations,
num_iterations_large=num_iterations_large,
)
input_size = len(input_png)
log(f"Input size: {input_size} ({input_size // 1024}K)")
result_size = len(result_png)
percentage = round(result_size / input_size * 100, 3)
log(
f"Result size: {result_size} ({result_size // 1024}K). "
f"Percentage of original: {percentage}%"
)
if result_size < input_size:
log("Result is smaller")
elif result_size == input_size:
log("Result has exact same size")
else:
if options.always_zopflify:
log("Original was smaller")
else:
log("Preserving original PNG since it was smaller")
# Set output file to input since zopfli didn't improve it.
result_png = input_png
if (
not options.overwrite
and os.path.isfile(options.outfile)
and input(f"File {options.outfile} exists, overwrite? (y/N)\n").strip().lower()
!= "y"
):
return 0
with open(options.outfile, "wb") as f:
f.write(result_png)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,12 @@
import zopfli
import zopfli.zopfli
def compress(data, **kwargs):
"""zlib.compress(data, **kwargs)
""" + zopfli.__COMPRESSOR_DOCSTRING__ + """
Returns:
String containing a zlib container
"""
kwargs['gzip_mode'] = 0
return zopfli.zopfli.compress(data, **kwargs)