Overhaul help, making use of long descriptions

This commit is contained in:
Andrew 2023-10-02 16:04:52 +13:00
parent 0b3302316b
commit cb9f6a25d8

View file

@ -35,13 +35,16 @@ use std::process::exit;
use std::time::Duration;
fn main() {
// Note: Long help descriptions should wrap at 70 characters (column 71)
// Indentation is 10 spaces, so this allows it to fit on an 80 character terminal
// Short help descriptions should ideally be no more than 54 characters
let matches = Command::new("oxipng")
.version(env!("CARGO_PKG_VERSION"))
.author("Joshua Holmer <jholmer.in@gmail.com>")
.about("Losslessly improves compression of PNG files")
.about("Losslessly improve compression of PNG files")
.arg(
Arg::new("files")
.help("File(s) to compress (use \"-\" for stdin)")
.help("File(s) to compress (use '-' for stdin)")
.index(1)
.num_args(1..)
.use_value_delimiter(false)
@ -50,11 +53,31 @@ fn main() {
)
.arg(
Arg::new("optimization")
.help("Optimization level - Default: 2")
.help("Optimization level (0-6, or max)")
.long_help("\
Set the optimization level preset. The default level 2 is quite fast
and provides good compression. Lower levels are faster, higher levels
provide better compression, though with increasingly diminishing
returns.
0 => --zc 5 --fast (1 trial, determined heuristically)
1 => --zc 10 --fast (1 trial, determined heuristically)
2 => --zc 11 -f 0,1,6,7 --fast (4 fast trials, 1 main trial)
3 => --zc 11 -f 0,7,8,9 (4 trials)
4 => --zc 12 -f 0,7,8,9 (4 trials)
5 => --zc 12 -f 0,1,2,5,6,7,8,9 (8 trials)
6 => --zc 12 -f 0-9 (10 trials)
max => (stable alias for the max level)
Manually specifying a compression option (zc, f, etc.) will override
the optimization preset, regardless of the order you write the
arguments.")
.short('o')
.long("opt")
.value_name("level")
.value_parser(["0", "1", "2", "3", "4", "5", "6", "max"]),
.default_value("2")
.value_parser(["0", "1", "2", "3", "4", "5", "6", "max"])
.hide_possible_values(true),
)
.arg(
Arg::new("backup")
@ -66,7 +89,10 @@ fn main() {
)
.arg(
Arg::new("recursive")
.help("Recurse into subdirectories and optimize all *.png/*.apng files")
.help("Recurse input directories, optimizing all PNG files")
.long_help("\
When directories are given as input, traverse the directory trees and
optimize all PNG files found (files with .png or .apng extension).")
.short('r')
.long("recursive")
.action(ArgAction::SetTrue),
@ -74,6 +100,10 @@ fn main() {
.arg(
Arg::new("output_dir")
.help("Write output file(s) to <directory>")
.long_help("\
Write output file(s) to <directory>. If the directory does not exist,
it will be created. Note that this will not preserve the directory
structure of the input files when used with '--recursive'.")
.long("dir")
.value_name("directory")
.value_parser(value_parser!(PathBuf))
@ -99,35 +129,54 @@ fn main() {
)
.arg(
Arg::new("preserve")
.help("Preserve file attributes if possible")
.help("Preserve file permissions and timestamps if possible")
.long_help("\
Preserve file permissions and timestamps if possible.
Timestamps can only be preserved if oxipng was compiled with the
`filetime` feature.")
.short('p')
.long("preserve")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("pretend")
.help("Do not write any files, only calculate compression gains")
.help("Do not write any files, only show compression results")
.short('P')
.long("pretend")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("strip-safe")
.help("Strip safely-removable metadata objects")
.help("Strip safely-removable chunks, same as '--strip safe'")
.short('s')
.action(ArgAction::SetTrue)
.conflicts_with("strip"),
)
.arg(
Arg::new("strip")
.help("Strip metadata objects ['safe', 'all', or comma-separated list]\nCAUTION: stripping 'all' will convert APNGs to standard PNGs")
.help("Strip metadata (safe, all, or comma-separated list)\nCAUTION: 'all' will convert APNGs to standard PNGs")
.long_help(format!("\
Strip metadata chunks, where <mode> is one of:
safe => Strip all non-critical chunks, except for the following:
{}
all => Strip all non-critical chunks
<list> => Strip chunks in the comma-separated list, e.g. 'bKGD,cHRM'
CAUTION: 'all' will convert APNGs to standard PNGs.",
StripChunks::KEEP_SAFE
.iter()
.map(|c| String::from_utf8_lossy(c))
.collect::<Vec<_>>()
.join(", ")))
.long("strip")
.value_name("mode")
.conflicts_with("strip-safe"),
)
.arg(
Arg::new("keep")
.help("Strip all optional metadata except objects in the comma-separated list")
.help("Strip all metadata except in the comma-separated list")
.long("keep")
.value_name("list")
.conflicts_with("strip")
@ -135,28 +184,50 @@ fn main() {
)
.arg(
Arg::new("alpha")
.help("Perform additional alpha optimizations")
.help("Perform additional alpha channel optimization")
.long_help("\
Perform additional optimization on images with an alpha channel, by
altering the color values of fully transparent pixels. This is
generally recommended for better compression, but take care as this is
technically a lossy transformation and may be unsuitable for some
applications.")
.short('a')
.long("alpha")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("interlace")
.help("PNG interlace type - Default: 0")
.help("Set PNG interlacing type (0, 1, keep)")
.long_help("\
Set the PNG interlacing type, where <type> is one of:
0 => Remove interlacing from all images that are processed
1 => Apply Adam7 interlacing on all images that are processed
keep => Keep the existing interlacing type of each image
Note that interlacing can add 25-50% to the size of an optimized
image. Only use it if you believe the benefits outweigh the costs for
your use case.")
.short('i')
.long("interlace")
.value_name("type")
.value_parser(["0", "1", "keep"]),
.default_value("0")
.value_parser(["0", "1", "keep"])
.hide_possible_values(true),
)
.arg(
Arg::new("scale16")
.help("Forcibly reduce 16-bit images to 8-bit")
.long_help("\
Forcibly reduce 16-bit images to 8-bit. Reduction is performed by
scaling the values, such that e.g. 0x00FF is reduced to 0x01 rather
than 0x00.")
.long("scale16")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("verbose")
.help("Run in verbose mode (use multiple times to increase verbosity)")
.help("Run in verbose mode (use twice to increase verbosity)")
.short('v')
.long("verbose")
.action(ArgAction::Count)
@ -172,9 +243,29 @@ fn main() {
)
.arg(
Arg::new("filters")
.help(format!("PNG delta filters (0-{})", RowFilter::LAST))
.help(format!("Filters to try (0-{}; see '--help' for details)", RowFilter::LAST))
.long_help("\
Peform compression trials with each of the given filter types. You can
specify a comma-separated list, or a range of values. E.g. '-f 0-3' is
the same as '-f 0,1,2,3'.
PNG delta filters (apply the same filter to every line)
0 => None (recommended to always include this filter)
1 => Sub
2 => Up
3 => Average
4 => Paeth
Heuristic strategies (try to find the best delta filter for each line)
5 => MinSum Minimum sum of absolute differences
6 => Entropy Highest Shannon entropy
7 => Bigrams Lowest count of distinct bigrams
8 => BigEnt Highest Shannon entropy of bigrams
9 => Brute Smallest compressed size (slow)
The default value depends on the optimization level preset.")
.short('f')
.long("filters")
.value_name("list")
.value_parser(|x: &str| {
parse_numeric_range_opts(x, 0, RowFilter::LAST)
.map_err(|_| "Invalid option for filters")
@ -182,7 +273,11 @@ fn main() {
)
.arg(
Arg::new("fast")
.help("Use fast filter evaluation (helpful when you have more filters enabled than CPU cores)")
.help("Use fast filter evaluation")
.long_help("\
Perform a fast compression evaluation of each enabled filter, followed
by a single main compression trial of the best result. Recommended
if you have more filters enabled than CPU cores.")
.long("fast")
.action(ArgAction::SetTrue),
)
@ -226,13 +321,21 @@ fn main() {
)
.arg(
Arg::new("no-recoding")
.help("No recoding of IDAT or other compressed chunks unless necessary")
.help("No recompression unless reductions occur")
.long_help("\
No recompression of IDAT unless reductions occur. Recompression of
other compressed chunks (such as iCCP) will also be disabled. Note
that the combination of '--nx' and '--nz' will fully disable all
optimization.")
.long("nz")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("fix")
.help("Enable error recovery")
.help("Disable checksum validation")
.long_help("\
Do not perform checksum validation of PNG chunks. This may allow some
files with errors to be processed successfully.")
.long("fix")
.action(ArgAction::SetTrue),
)
@ -244,53 +347,46 @@ fn main() {
)
.arg(
Arg::new("zopfli")
.help("Use the slow but stronger Zopfli compressor (recommended use is with all filters and `--fast` enabled)")
.help("Use the slow but stronger Zopfli compressor")
.long_help("\
Use the slow but stronger Zopfli compressor. Recommended use is with
'-o max' and '--fast'.
This option is only available if oxipng was compiled with the `zopfli`
feature.")
.short('Z')
.long("zopfli")
.action(ArgAction::SetTrue),
)
.arg(
Arg::new("timeout")
.help("Maximum amount of time, in seconds, to spend on optimizations (currently of limited use due to the shift away from zlib)")
.help("Maximum amount of time to spend on optimizations")
.long_help("\
Maximum amount of time, in seconds, to spend on optimizations. Oxipng
will check the timeout before each reduction or compression trial, and
will stop trying to optimize the file if the timeout is exceeded. Note
that this does not cut short any compression trials that are already
in progress, so it is currently of limited effectiveness for large
files or high compression levels.")
.value_name("secs")
.long("timeout")
.value_parser(value_parser!(u64)),
)
.arg(
Arg::new("threads")
.help("Set number of threads to use - Default: num CPU cores")
.help("Set number of threads to use [default: num CPU cores]")
.long_help("\
Set number of threads to use [default: num CPU cores]
This option is only available if oxipng was compiled with the
`parallel` feature.")
.long("threads")
.short('t')
.value_name("num")
.value_parser(value_parser!(usize)),
)
.after_help(
"Optimization levels:
-o 0 => --zc 5 --fast (1 trial, determined heuristically)
-o 1 => --zc 10 --fast (1 trial, determined heuristically)
-o 2 => --zc 11 -f 0,1,6,7 --fast (1 trial, determined by fast evaluation)
-o 3 => --zc 11 -f 0,7,8,9 (4 trials)
-o 4 => --zc 12 -f 0,7,8,9 (4 trials; same as `-o 3` for zopfli)
-o 5 => --zc 12 -f 0,1,2,5,6,7,8,9 (8 trials)
-o 6 => --zc 12 -f 0-9 (10 trials)
-o max => (stable alias for the max compression)
Manually specifying a compression option (zc, f, etc.) will override the optimization preset,
regardless of the order you write the arguments.
PNG delta filters:
0 => None
1 => Sub
2 => Up
3 => Average
4 => Paeth
Heuristic filter selection strategies:
5 => MinSum Minimum sum of absolute differences
6 => Entropy Highest Shannon entropy
7 => Bigrams Lowest count of distinct bigrams
8 => BigEnt Highest Shannon entropy of bigrams
9 => Brute Smallest compressed size (slow)",
)
.after_help("Run `oxipng --help` to see full details of all options")
.after_long_help("")
.get_matches_from(std::env::args());
if matches.get_flag("backup") {