mirror of
https://github.com/gen2brain/cbconvert
synced 2026-06-30 09:11:54 +02:00
Add ZipLevel, issue #48
This commit is contained in:
@@ -23,6 +23,8 @@ type Options struct {
|
||||
Format string
|
||||
// Archive format, valid values are zip, tar
|
||||
Archive string
|
||||
// ZIP compression level: -1 default, 0 store (no compression), 1-9 deflate (1 fastest, 9 smallest)
|
||||
ZipLevel int
|
||||
// JPEG image quality
|
||||
Quality int
|
||||
// Encoder speed/effort, format-specific: webp method 0-6, avif speed 0-10, jxl effort 1-10; -1 uses the format default
|
||||
@@ -137,6 +139,7 @@ func NewOptions() Options {
|
||||
o.Archive = "zip"
|
||||
o.Quality = 75
|
||||
o.Effort = -1
|
||||
o.ZipLevel = -1
|
||||
o.Filter = 2
|
||||
|
||||
return o
|
||||
|
||||
@@ -3,6 +3,7 @@ package cbconvert
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/flate"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -50,6 +51,13 @@ func (c *Converter) archiveSaveZip(fileName string) error {
|
||||
|
||||
z := zip.NewWriter(zipFile)
|
||||
|
||||
if c.Opts.ZipLevel >= 1 {
|
||||
level := c.Opts.ZipLevel
|
||||
z.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
|
||||
return flate.NewWriter(out, level)
|
||||
})
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(c.Workdir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
||||
@@ -72,6 +80,9 @@ func (c *Converter) archiveSaveZip(fileName string) error {
|
||||
}
|
||||
|
||||
zipInfo.Method = zip.Deflate
|
||||
if c.Opts.ZipLevel == 0 {
|
||||
zipInfo.Method = zip.Store
|
||||
}
|
||||
w, err := z.CreateHeader(zipInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
||||
|
||||
@@ -203,6 +203,58 @@ func TestConvertTar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestZipLevel(t *testing.T) {
|
||||
convertWith := func(level int) *zip.ReadCloser {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { os.RemoveAll(tmpDir) })
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.ZipLevel = level
|
||||
opts.NoConvert = true
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbz"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
zr, err := zip.OpenReader(filepath.Join(tmpDir, "test.cbz"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { zr.Close() })
|
||||
|
||||
return zr
|
||||
}
|
||||
|
||||
store := convertWith(0)
|
||||
for _, f := range store.File {
|
||||
if f.Method != zip.Store {
|
||||
t.Errorf("level 0: %s stored with method %d, want Store", f.Name, f.Method)
|
||||
}
|
||||
if f.CompressedSize64 != f.UncompressedSize64 {
|
||||
t.Errorf("level 0: %s is compressed (%d < %d)", f.Name, f.CompressedSize64, f.UncompressedSize64)
|
||||
}
|
||||
}
|
||||
|
||||
deflate := convertWith(9)
|
||||
for _, f := range deflate.File {
|
||||
if f.Method != zip.Deflate {
|
||||
t.Errorf("level 9: %s method %d, want Deflate", f.Name, f.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageTransforms(t *testing.T) {
|
||||
conv := New(NewOptions())
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ func options() cbconvert.Options {
|
||||
opts.NoConvert = iup.GetHandle("NoConvert").GetAttribute("VALUE") == "ON"
|
||||
opts.NoNonImage = iup.GetHandle("NoNonImage").GetAttribute("VALUE") == "ON"
|
||||
opts.Archive = strings.ToLower(iup.GetHandle("Archive").GetAttribute("VALUESTRING"))
|
||||
opts.ZipLevel = zipLevel(iup.GetHandle("ZipLevel").GetAttribute("VALUESTRING"))
|
||||
opts.Format = strings.ToLower(iup.GetHandle("Format").GetAttribute("VALUESTRING"))
|
||||
opts.Width = iup.GetHandle("Width").GetInt("VALUE")
|
||||
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
||||
@@ -246,6 +247,29 @@ func setActive() {
|
||||
} else {
|
||||
iup.GetHandle("VboxOutFile").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
|
||||
if opts.Archive == "zip" {
|
||||
iup.GetHandle("VboxZipLevel").SetAttribute("ACTIVE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("VboxZipLevel").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
}
|
||||
|
||||
// zipLevel maps the compression dropdown selection to Options.ZipLevel.
|
||||
func zipLevel(value string) int {
|
||||
switch value {
|
||||
case "Default":
|
||||
return -1
|
||||
case "Store (none)":
|
||||
return 0
|
||||
default:
|
||||
level, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
func setEffort(format string) {
|
||||
@@ -521,8 +545,32 @@ func tabs() iup.Ihandle {
|
||||
"VALUE": "1",
|
||||
"1": "ZIP",
|
||||
"2": "TAR",
|
||||
}).SetHandle("Archive"),
|
||||
}).SetHandle("Archive").
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label("Compression:"),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"VALUE": "1",
|
||||
"1": "Default",
|
||||
"2": "Store (none)",
|
||||
"3": "1",
|
||||
"4": "2",
|
||||
"5": "3",
|
||||
"6": "4",
|
||||
"7": "5",
|
||||
"8": "6",
|
||||
"9": "7",
|
||||
"10": "8",
|
||||
"11": "9",
|
||||
}).SetHandle("ZipLevel").
|
||||
SetAttribute("TIP", "ZIP compression: Store disables it, 1 is fastest, 9 is smallest"),
|
||||
).SetHandle("VboxZipLevel"),
|
||||
).SetAttributes("NGAP=10"),
|
||||
iup.Space().SetAttribute("SIZE", "15"),
|
||||
iup.Vbox(
|
||||
|
||||
@@ -177,6 +177,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
convert.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl")
|
||||
convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar")
|
||||
convert.IntVar(&opts.ZipLevel, "zip-level", -1, "ZIP compression level, 0 disables compression, 1-9 sets deflate level (1 fastest, 9 smallest), -1 uses the default")
|
||||
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||
convert.IntVar(&opts.Effort, "effort", -1, "Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default")
|
||||
convert.BoolVar(&opts.Lossless, "lossless", false, "Lossless compression (webp, avif, jxl), ignores quality")
|
||||
@@ -235,7 +236,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s <command> [<flags>] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0]))
|
||||
fmt.Fprintf(os.Stderr, "\nCommands:\n")
|
||||
fmt.Fprintf(os.Stderr, "\n convert\n \tConvert archive or document\n\n")
|
||||
order := []string{"width", "height", "fit", "format", "archive", "quality", "effort", "lossless", "combine", "outfile", "filter", "no-cover", "no-rgb",
|
||||
order := []string{"width", "height", "fit", "format", "archive", "zip-level", "quality", "effort", "lossless", "combine", "outfile", "filter", "no-cover", "no-rgb",
|
||||
"no-nonimage", "no-convert", "grayscale", "rotate", "brightness", "contrast", "suffix", "outdir", "size", "recursive", "quiet"}
|
||||
for _, name := range order {
|
||||
f := convert.Lookup(name)
|
||||
|
||||
Reference in New Issue
Block a user