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
|
Format string
|
||||||
// Archive format, valid values are zip, tar
|
// Archive format, valid values are zip, tar
|
||||||
Archive string
|
Archive string
|
||||||
|
// ZIP compression level: -1 default, 0 store (no compression), 1-9 deflate (1 fastest, 9 smallest)
|
||||||
|
ZipLevel int
|
||||||
// JPEG image quality
|
// JPEG image quality
|
||||||
Quality int
|
Quality int
|
||||||
// Encoder speed/effort, format-specific: webp method 0-6, avif speed 0-10, jxl effort 1-10; -1 uses the format default
|
// 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.Archive = "zip"
|
||||||
o.Quality = 75
|
o.Quality = 75
|
||||||
o.Effort = -1
|
o.Effort = -1
|
||||||
|
o.ZipLevel = -1
|
||||||
o.Filter = 2
|
o.Filter = 2
|
||||||
|
|
||||||
return o
|
return o
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package cbconvert
|
|||||||
import (
|
import (
|
||||||
"archive/tar"
|
"archive/tar"
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
"compress/flate"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@@ -50,6 +51,13 @@ func (c *Converter) archiveSaveZip(fileName string) error {
|
|||||||
|
|
||||||
z := zip.NewWriter(zipFile)
|
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)
|
files, err := os.ReadDir(c.Workdir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
return fmt.Errorf("archiveSaveZip: %w", err)
|
||||||
@@ -72,6 +80,9 @@ func (c *Converter) archiveSaveZip(fileName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
zipInfo.Method = zip.Deflate
|
zipInfo.Method = zip.Deflate
|
||||||
|
if c.Opts.ZipLevel == 0 {
|
||||||
|
zipInfo.Method = zip.Store
|
||||||
|
}
|
||||||
w, err := z.CreateHeader(zipInfo)
|
w, err := z.CreateHeader(zipInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
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) {
|
func TestImageTransforms(t *testing.T) {
|
||||||
conv := New(NewOptions())
|
conv := New(NewOptions())
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ func options() cbconvert.Options {
|
|||||||
opts.NoConvert = iup.GetHandle("NoConvert").GetAttribute("VALUE") == "ON"
|
opts.NoConvert = iup.GetHandle("NoConvert").GetAttribute("VALUE") == "ON"
|
||||||
opts.NoNonImage = iup.GetHandle("NoNonImage").GetAttribute("VALUE") == "ON"
|
opts.NoNonImage = iup.GetHandle("NoNonImage").GetAttribute("VALUE") == "ON"
|
||||||
opts.Archive = strings.ToLower(iup.GetHandle("Archive").GetAttribute("VALUESTRING"))
|
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.Format = strings.ToLower(iup.GetHandle("Format").GetAttribute("VALUESTRING"))
|
||||||
opts.Width = iup.GetHandle("Width").GetInt("VALUE")
|
opts.Width = iup.GetHandle("Width").GetInt("VALUE")
|
||||||
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
||||||
@@ -246,6 +247,29 @@ func setActive() {
|
|||||||
} else {
|
} else {
|
||||||
iup.GetHandle("VboxOutFile").SetAttribute("ACTIVE", "NO")
|
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) {
|
func setEffort(format string) {
|
||||||
@@ -521,8 +545,32 @@ func tabs() iup.Ihandle {
|
|||||||
"VALUE": "1",
|
"VALUE": "1",
|
||||||
"1": "ZIP",
|
"1": "ZIP",
|
||||||
"2": "TAR",
|
"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"),
|
).SetAttributes("NGAP=10"),
|
||||||
iup.Space().SetAttribute("SIZE", "15"),
|
iup.Space().SetAttribute("SIZE", "15"),
|
||||||
iup.Vbox(
|
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.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.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.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.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.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")
|
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, "Usage: %s <command> [<flags>] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0]))
|
||||||
fmt.Fprintf(os.Stderr, "\nCommands:\n")
|
fmt.Fprintf(os.Stderr, "\nCommands:\n")
|
||||||
fmt.Fprintf(os.Stderr, "\n convert\n \tConvert archive or document\n\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"}
|
"no-nonimage", "no-convert", "grayscale", "rotate", "brightness", "contrast", "suffix", "outdir", "size", "recursive", "quiet"}
|
||||||
for _, name := range order {
|
for _, name := range order {
|
||||||
f := convert.Lookup(name)
|
f := convert.Lookup(name)
|
||||||
|
|||||||
Reference in New Issue
Block a user