mirror of
https://github.com/gen2brain/cbconvert
synced 2026-07-24 12:25:38 +02:00
Compare commits
3
Commits
a8b2f65ca5
...
0439a2edde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0439a2edde | ||
|
|
92c49290ae | ||
|
|
7f025aa9c2 |
@@ -70,6 +70,8 @@ Commands:
|
|||||||
Image height (default "0")
|
Image height (default "0")
|
||||||
--fit
|
--fit
|
||||||
Best fit for required width and height (default "false")
|
Best fit for required width and height (default "false")
|
||||||
|
--no-upscale
|
||||||
|
Do not upscale images already smaller than the requested width/height (default "false")
|
||||||
--dpi
|
--dpi
|
||||||
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
||||||
--format
|
--format
|
||||||
@@ -126,6 +128,8 @@ Commands:
|
|||||||
Image height (default "0")
|
Image height (default "0")
|
||||||
--fit
|
--fit
|
||||||
Best fit for required width and height (default "false")
|
Best fit for required width and height (default "false")
|
||||||
|
--no-upscale
|
||||||
|
Do not upscale images already smaller than the requested width/height (default "false")
|
||||||
--dpi
|
--dpi
|
||||||
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
||||||
--format
|
--format
|
||||||
@@ -156,6 +160,8 @@ Commands:
|
|||||||
Image height (default "0")
|
Image height (default "0")
|
||||||
--fit
|
--fit
|
||||||
Best fit for required width and height (default "false")
|
Best fit for required width and height (default "false")
|
||||||
|
--no-upscale
|
||||||
|
Do not upscale images already smaller than the requested width/height (default "false")
|
||||||
--dpi
|
--dpi
|
||||||
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
||||||
--filter
|
--filter
|
||||||
|
|||||||
+5
-10
@@ -39,6 +39,8 @@ type Options struct {
|
|||||||
Height int
|
Height int
|
||||||
// Best fit for required width and height
|
// Best fit for required width and height
|
||||||
Fit bool
|
Fit bool
|
||||||
|
// Do not upscale images already smaller than the requested width/height
|
||||||
|
NoUpscale bool
|
||||||
// Document rendering resolution in DPI (PDF, EPUB, etc.); 0 uses the default
|
// Document rendering resolution in DPI (PDF, EPUB, etc.); 0 uses the default
|
||||||
DPI int
|
DPI int
|
||||||
// 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
// 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||||
@@ -180,6 +182,7 @@ func (o Options) Args() []string {
|
|||||||
num("width", o.Width, def.Width)
|
num("width", o.Width, def.Width)
|
||||||
num("height", o.Height, def.Height)
|
num("height", o.Height, def.Height)
|
||||||
flag("fit", o.Fit)
|
flag("fit", o.Fit)
|
||||||
|
flag("no-upscale", o.NoUpscale)
|
||||||
num("dpi", o.DPI, def.DPI)
|
num("dpi", o.DPI, def.DPI)
|
||||||
str("format", o.Format, def.Format)
|
str("format", o.Format, def.Format)
|
||||||
str("archive", o.Archive, def.Archive)
|
str("archive", o.Archive, def.Archive)
|
||||||
@@ -360,11 +363,7 @@ func (c *Converter) Cover(file File) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
||||||
if c.Opts.Fit {
|
cover = c.resizeFit(cover)
|
||||||
cover = fit(cover, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
|
||||||
} else {
|
|
||||||
cover = resize(cover, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext := c.Opts.Format
|
ext := c.Opts.Format
|
||||||
@@ -409,11 +408,7 @@ func (c *Converter) Thumbnail(file File) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
||||||
if c.Opts.Fit {
|
cover = c.resizeFit(cover)
|
||||||
cover = fit(cover, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
|
||||||
} else {
|
|
||||||
cover = resize(cover, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
cover = resize(cover, 256, 0, filters[c.Opts.Filter])
|
cover = resize(cover, 256, 0, filters[c.Opts.Filter])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,11 +326,7 @@ func (c *Converter) imageTransform(img image.Image) image.Image {
|
|||||||
var i = img
|
var i = img
|
||||||
|
|
||||||
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
||||||
if c.Opts.Fit {
|
i = c.resizeFit(i)
|
||||||
i = fit(i, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
|
||||||
} else {
|
|
||||||
i = resize(i, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Opts.Rotate > 0 {
|
if c.Opts.Rotate > 0 {
|
||||||
|
|||||||
@@ -86,6 +86,26 @@ func fit(img image.Image, width, height int, filter transform.ResampleFilter) *i
|
|||||||
return resize(img, dstW, dstH, filter)
|
return resize(img, dstW, dstH, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// withinBounds reports whether img already fits within width by height; a zero dimension is unbounded.
|
||||||
|
func withinBounds(img image.Image, width, height int) bool {
|
||||||
|
b := img.Bounds()
|
||||||
|
|
||||||
|
return (width == 0 || b.Dx() <= width) && (height == 0 || b.Dy() <= height)
|
||||||
|
}
|
||||||
|
|
||||||
|
// resizeFit resizes img to the configured width/height, honoring Fit and NoUpscale.
|
||||||
|
func (c *Converter) resizeFit(img image.Image) image.Image {
|
||||||
|
if c.Opts.Fit {
|
||||||
|
return fit(img, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Opts.NoUpscale && withinBounds(img, c.Opts.Width, c.Opts.Height) {
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
return resize(img, c.Opts.Width, c.Opts.Height, filters[c.Opts.Filter])
|
||||||
|
}
|
||||||
|
|
||||||
func rotate(img image.Image, angle float64) *image.RGBA {
|
func rotate(img image.Image, angle float64) *image.RGBA {
|
||||||
return transform.Rotate(img, angle, &transform.RotationOptions{ResizeBounds: true, Pivot: &image.Point{}})
|
return transform.Rotate(img, angle, &transform.RotationOptions{ResizeBounds: true, Pivot: &image.Point{}})
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-1
@@ -114,17 +114,68 @@ func TestArgs(t *testing.T) {
|
|||||||
opts.Effort = 4
|
opts.Effort = 4
|
||||||
opts.Lossless = true
|
opts.Lossless = true
|
||||||
opts.Width = 1200
|
opts.Width = 1200
|
||||||
|
opts.NoUpscale = true
|
||||||
opts.DPI = 150
|
opts.DPI = 150
|
||||||
opts.Grayscale = true
|
opts.Grayscale = true
|
||||||
opts.OutDir = "/out"
|
opts.OutDir = "/out"
|
||||||
|
|
||||||
got := strings.Join(opts.Args(), " ")
|
got := strings.Join(opts.Args(), " ")
|
||||||
want := "--width 1200 --dpi 150 --format webp --quality 90 --effort 4 --lossless --grayscale --outdir /out"
|
want := "--width 1200 --no-upscale --dpi 150 --format webp --quality 90 --effort 4 --lossless --grayscale --outdir /out"
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf("Args() = %q, want %q", got, want)
|
t.Errorf("Args() = %q, want %q", got, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoUpscale(t *testing.T) {
|
||||||
|
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
width := func(o Options) int {
|
||||||
|
conv := New(o)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstPage(t, conv, filepath.Join(tmpDir, "test.cbz")).Bounds().Dx()
|
||||||
|
}
|
||||||
|
|
||||||
|
base := NewOptions()
|
||||||
|
base.OutDir = tmpDir
|
||||||
|
orig := width(base)
|
||||||
|
|
||||||
|
up := NewOptions()
|
||||||
|
up.OutDir = tmpDir
|
||||||
|
up.Width = orig * 2
|
||||||
|
up.NoUpscale = true
|
||||||
|
if got := width(up); got != orig {
|
||||||
|
t.Errorf("NoUpscale should keep original width %d, got %d", orig, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
no := NewOptions()
|
||||||
|
no.OutDir = tmpDir
|
||||||
|
no.Width = orig * 2
|
||||||
|
if got := width(no); got != orig*2 {
|
||||||
|
t.Errorf("without NoUpscale should upscale to %d, got %d", orig*2, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
down := NewOptions()
|
||||||
|
down.OutDir = tmpDir
|
||||||
|
down.Width = orig / 2
|
||||||
|
down.NoUpscale = true
|
||||||
|
if got := width(down); got != orig/2 {
|
||||||
|
t.Errorf("NoUpscale should still downscale to %d, got %d", orig/2, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestConvertDPI(t *testing.T) {
|
func TestConvertDPI(t *testing.T) {
|
||||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/gen2brain/iup-go/iup"
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gen2brain/cbconvert"
|
||||||
|
"github.com/gen2brain/iup-go/iup"
|
||||||
|
)
|
||||||
|
|
||||||
func fileDlg(title string, multiple, directory bool, dirKey string) ([]string, error) {
|
func fileDlg(title string, multiple, directory bool, dirKey string) ([]string, error) {
|
||||||
ret := make([]string, 0)
|
ret := make([]string, 0)
|
||||||
@@ -21,7 +28,12 @@ func fileDlg(title string, multiple, directory bool, dirKey string) ([]string, e
|
|||||||
"EXTFILTER": "Comic Files|*.rar;*.zip;*.7z;*.tar;*.cbr;*.cbz;*.cb7;*.cbt;*.pdf;*.epub;*.mobi;*.docx;*.pptx|",
|
"EXTFILTER": "Comic Files|*.rar;*.zip;*.7z;*.tar;*.cbr;*.cbz;*.cb7;*.cbt;*.pdf;*.epub;*.mobi;*.docx;*.pptx|",
|
||||||
"FILTER": "*.cb*", // for Motif
|
"FILTER": "*.cb*", // for Motif
|
||||||
"TITLE": title,
|
"TITLE": title,
|
||||||
|
"SHOWPREVIEW": "YES",
|
||||||
|
"PREVIEWWIDTH": "240",
|
||||||
|
"PREVIEWHEIGHT": "320",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
dlg.SetCallback("FILE_CB", iup.FileFunc(previewCover()))
|
||||||
} else {
|
} else {
|
||||||
dlg.SetAttributes(map[string]string{
|
dlg.SetAttributes(map[string]string{
|
||||||
"DIALOGTYPE": "DIR",
|
"DIALOGTYPE": "DIR",
|
||||||
@@ -55,6 +67,86 @@ func fileDlg(title string, multiple, directory bool, dirKey string) ([]string, e
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const dlgPreviewName = "_FILEDLGPREVIEW_"
|
||||||
|
|
||||||
|
// previewCover returns a FILE_CB handler that draws the highlighted comic's cover in the dialog preview pane.
|
||||||
|
func previewCover() iup.FileFunc {
|
||||||
|
var image iup.Ihandle
|
||||||
|
var lastFile string
|
||||||
|
|
||||||
|
return func(ih iup.Ihandle, filename, status string) int {
|
||||||
|
switch status {
|
||||||
|
case "PAINT":
|
||||||
|
iup.DrawBegin(ih)
|
||||||
|
cw, ch := iup.DrawGetSize(ih)
|
||||||
|
iup.DrawParentBackground(ih)
|
||||||
|
|
||||||
|
if filename != lastFile {
|
||||||
|
lastFile = filename
|
||||||
|
if image != 0 {
|
||||||
|
image.Destroy()
|
||||||
|
image = 0
|
||||||
|
}
|
||||||
|
if img := loadCover(filename, cw, ch); img != 0 {
|
||||||
|
image = img
|
||||||
|
iup.SetHandle(dlgPreviewName, image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if image != 0 {
|
||||||
|
iw, iih, _ := iup.DrawGetImageInfo(dlgPreviewName)
|
||||||
|
iup.DrawImage(ih, dlgPreviewName, (cw-iw)/2, (ch-iih)/2, iw, iih)
|
||||||
|
} else {
|
||||||
|
ih.SetAttribute("DRAWCOLOR", "128 128 128")
|
||||||
|
tw, th := iup.DrawGetTextSize(ih, "No preview")
|
||||||
|
iup.DrawText(ih, "No preview", (cw-tw)/2, (ch-th)/2, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
iup.DrawEnd(ih)
|
||||||
|
case "FINISH":
|
||||||
|
if image != 0 {
|
||||||
|
image.Destroy()
|
||||||
|
image = 0
|
||||||
|
}
|
||||||
|
lastFile = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return iup.DEFAULT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadCover extracts the cover of a comic file and returns it as an IUP image fitted to w by h, or 0.
|
||||||
|
func loadCover(path string, w, h int) iup.Ihandle {
|
||||||
|
if w <= 0 || h <= 0 || !isComic(path) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := os.Stat(path)
|
||||||
|
if err != nil || fi.IsDir() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := cbconvert.NewOptions()
|
||||||
|
opts.DPI = 96
|
||||||
|
|
||||||
|
img, err := cbconvert.New(opts).Preview(path, fi, w, h)
|
||||||
|
if err != nil || img.Image == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return iup.ImageFromImage(img.Image)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isComic(path string) bool {
|
||||||
|
switch strings.ToLower(filepath.Ext(path)) {
|
||||||
|
case ".rar", ".zip", ".7z", ".tar", ".cbr", ".cbz", ".cb7", ".cbt",
|
||||||
|
".pdf", ".xps", ".epub", ".mobi", ".docx", ".pptx", ".xlsx":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func saveDlg(title, dirKey string) string {
|
func saveDlg(title, dirKey string) string {
|
||||||
dlg := iup.FileDlg()
|
dlg := iup.FileDlg()
|
||||||
defer dlg.Destroy()
|
defer dlg.Destroy()
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ func options() cbconvert.Options {
|
|||||||
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
||||||
opts.DPI = dpiValue(iup.GetHandle("DPI").GetAttribute("VALUE"))
|
opts.DPI = dpiValue(iup.GetHandle("DPI").GetAttribute("VALUE"))
|
||||||
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
||||||
|
opts.NoUpscale = iup.GetHandle("NoUpscale").GetAttribute("VALUE") == "ON"
|
||||||
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
||||||
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
||||||
switch opts.Format {
|
switch opts.Format {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ var settings = []setting{
|
|||||||
{"NoNonImage", kindBool, "OFF"},
|
{"NoNonImage", kindBool, "OFF"},
|
||||||
{"Combine", kindBool, "OFF"},
|
{"Combine", kindBool, "OFF"},
|
||||||
{"Fit", kindBool, "OFF"},
|
{"Fit", kindBool, "OFF"},
|
||||||
|
{"NoUpscale", kindBool, "OFF"},
|
||||||
{"Lossless", kindBool, "OFF"},
|
{"Lossless", kindBool, "OFF"},
|
||||||
{"Grayscale", kindBool, "OFF"},
|
{"Grayscale", kindBool, "OFF"},
|
||||||
{"OutDir", kindStr, ""},
|
{"OutDir", kindStr, ""},
|
||||||
|
|||||||
@@ -359,6 +359,8 @@ func tabImage() iup.Ihandle {
|
|||||||
iup.Vbox(
|
iup.Vbox(
|
||||||
iup.Toggle(" Best Fit").SetHandle("Fit").
|
iup.Toggle(" Best Fit").SetHandle("Fit").
|
||||||
SetAttributes(`TIP="Best fit for required width and height"`),
|
SetAttributes(`TIP="Best fit for required width and height"`),
|
||||||
|
iup.Toggle(" No Upscale").SetHandle("NoUpscale").
|
||||||
|
SetAttribute("TIP", "Do not enlarge images already smaller than the requested size"),
|
||||||
),
|
),
|
||||||
iup.Vbox(
|
iup.Vbox(
|
||||||
iup.Label("Resize Filter:"),
|
iup.Label("Resize Filter:"),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ go 1.26
|
|||||||
require (
|
require (
|
||||||
github.com/gen2brain/cbconvert v1.0.5-0.20260623161611-a5817c3ba5de
|
github.com/gen2brain/cbconvert v1.0.5-0.20260623161611-a5817c3ba5de
|
||||||
github.com/schollz/progressbar/v3 v3.19.0
|
github.com/schollz/progressbar/v3 v3.19.0
|
||||||
|
golang.org/x/term v0.44.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -52,7 +53,6 @@ require (
|
|||||||
golang.org/x/net v0.56.0 // indirect
|
golang.org/x/net v0.56.0 // indirect
|
||||||
golang.org/x/sync v0.21.0 // indirect
|
golang.org/x/sync v0.21.0 // indirect
|
||||||
golang.org/x/sys v0.46.0 // indirect
|
golang.org/x/sys v0.46.0 // indirect
|
||||||
golang.org/x/term v0.44.0 // indirect
|
|
||||||
golang.org/x/text v0.38.0 // indirect
|
golang.org/x/text v0.38.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
+44
-24
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gen2brain/cbconvert"
|
"github.com/gen2brain/cbconvert"
|
||||||
pb "github.com/schollz/progressbar/v3"
|
pb "github.com/schollz/progressbar/v3"
|
||||||
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
var appVersion string
|
var appVersion string
|
||||||
@@ -78,39 +79,55 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interactive := !opts.Quiet && term.IsTerminal(int(os.Stderr.Fd()))
|
||||||
|
|
||||||
var bar *pb.ProgressBar
|
var bar *pb.ProgressBar
|
||||||
if opts.Cover || opts.Thumbnail || opts.Meta {
|
newBar := func(max int, description string) {
|
||||||
if !opts.Quiet {
|
bar = pb.NewOptions(max,
|
||||||
bar = pb.NewOptions(conv.Nfiles,
|
pb.OptionShowCount(),
|
||||||
pb.OptionShowCount(),
|
pb.OptionClearOnFinish(),
|
||||||
pb.OptionClearOnFinish(),
|
pb.OptionUseANSICodes(true),
|
||||||
pb.OptionUseANSICodes(true),
|
pb.OptionSetPredictTime(false),
|
||||||
pb.OptionSetPredictTime(false),
|
pb.OptionSetDescription(description),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
clearLine := func() {
|
||||||
|
fmt.Fprint(os.Stderr, "\033[2K\r")
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup := func() {
|
||||||
|
if interactive {
|
||||||
|
if bar != nil {
|
||||||
|
_ = bar.Finish()
|
||||||
|
}
|
||||||
|
clearLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if interactive && (opts.Cover || opts.Thumbnail || opts.Meta) {
|
||||||
|
newBar(conv.Nfiles, "")
|
||||||
|
}
|
||||||
|
|
||||||
conv.OnStart = func() {
|
conv.OnStart = func() {
|
||||||
if !opts.Quiet {
|
if interactive {
|
||||||
bar = pb.NewOptions(conv.Ncontents,
|
clearLine()
|
||||||
pb.OptionShowCount(),
|
newBar(conv.Ncontents, fmt.Sprintf("Converting %d of %d:", conv.CurrFile, conv.Nfiles))
|
||||||
pb.OptionClearOnFinish(),
|
|
||||||
pb.OptionUseANSICodes(true),
|
|
||||||
pb.OptionSetDescription(fmt.Sprintf("Converting %d of %d:", conv.CurrFile, conv.Nfiles)),
|
|
||||||
pb.OptionSetPredictTime(false),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conv.OnProgress = func() {
|
conv.OnProgress = func() {
|
||||||
if !opts.Quiet {
|
if bar != nil {
|
||||||
_ = bar.Add(1)
|
_ = bar.Add(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conv.OnCompress = func() {
|
conv.OnCompress = func() {
|
||||||
if !opts.Quiet {
|
if interactive {
|
||||||
fmt.Fprintf(os.Stderr, "Compressing %d of %d...\r", conv.CurrFile, conv.Nfiles)
|
if bar != nil {
|
||||||
|
_ = bar.Finish()
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "Compressing %d of %d...", conv.CurrFile, conv.Nfiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +137,7 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "\r")
|
cleanup()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -163,7 +180,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "\r")
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseFlags parses command line flags.
|
// parseFlags parses command line flags.
|
||||||
@@ -175,6 +192,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
convert.IntVar(&opts.Width, "width", 0, "Image width")
|
convert.IntVar(&opts.Width, "width", 0, "Image width")
|
||||||
convert.IntVar(&opts.Height, "height", 0, "Image height")
|
convert.IntVar(&opts.Height, "height", 0, "Image height")
|
||||||
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.BoolVar(&opts.NoUpscale, "no-upscale", false, "Do not upscale images already smaller than the requested width/height")
|
||||||
convert.IntVar(&opts.DPI, "dpi", 0, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300)")
|
convert.IntVar(&opts.DPI, "dpi", 0, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300)")
|
||||||
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")
|
||||||
@@ -203,6 +221,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
cover.IntVar(&opts.Width, "width", 0, "Image width")
|
cover.IntVar(&opts.Width, "width", 0, "Image width")
|
||||||
cover.IntVar(&opts.Height, "height", 0, "Image height")
|
cover.IntVar(&opts.Height, "height", 0, "Image height")
|
||||||
cover.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
cover.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||||
|
cover.BoolVar(&opts.NoUpscale, "no-upscale", false, "Do not upscale images already smaller than the requested width/height")
|
||||||
cover.IntVar(&opts.DPI, "dpi", 0, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300)")
|
cover.IntVar(&opts.DPI, "dpi", 0, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300)")
|
||||||
cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
||||||
cover.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
cover.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||||
@@ -218,6 +237,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
thumbnail.IntVar(&opts.Width, "width", 0, "Image width")
|
thumbnail.IntVar(&opts.Width, "width", 0, "Image width")
|
||||||
thumbnail.IntVar(&opts.Height, "height", 0, "Image height")
|
thumbnail.IntVar(&opts.Height, "height", 0, "Image height")
|
||||||
thumbnail.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
thumbnail.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||||
|
thumbnail.BoolVar(&opts.NoUpscale, "no-upscale", false, "Do not upscale images already smaller than the requested width/height")
|
||||||
thumbnail.IntVar(&opts.DPI, "dpi", 0, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300)")
|
thumbnail.IntVar(&opts.DPI, "dpi", 0, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300)")
|
||||||
thumbnail.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
thumbnail.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
||||||
thumbnail.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
|
thumbnail.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
|
||||||
@@ -239,7 +259,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", "dpi", "format", "archive", "zip-level", "quality", "effort", "lossless", "combine", "outfile", "filter", "no-cover", "no-rgb",
|
order := []string{"width", "height", "fit", "no-upscale", "dpi", "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)
|
||||||
@@ -247,14 +267,14 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "\n cover\n \tExtract cover\n\n")
|
fmt.Fprintf(os.Stderr, "\n cover\n \tExtract cover\n\n")
|
||||||
order = []string{"width", "height", "fit", "dpi", "format", "quality", "effort", "lossless", "filter", "outdir", "size", "recursive", "quiet"}
|
order = []string{"width", "height", "fit", "no-upscale", "dpi", "format", "quality", "effort", "lossless", "filter", "outdir", "size", "recursive", "quiet"}
|
||||||
for _, name := range order {
|
for _, name := range order {
|
||||||
f := cover.Lookup(name)
|
f := cover.Lookup(name)
|
||||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||||
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(os.Stderr, "\n thumbnail\n \tExtract cover thumbnail (freedesktop spec.)\n\n")
|
fmt.Fprintf(os.Stderr, "\n thumbnail\n \tExtract cover thumbnail (freedesktop spec.)\n\n")
|
||||||
order = []string{"width", "height", "fit", "dpi", "filter", "outdir", "outfile", "size", "recursive", "quiet"}
|
order = []string{"width", "height", "fit", "no-upscale", "dpi", "filter", "outdir", "outfile", "size", "recursive", "quiet"}
|
||||||
for _, name := range order {
|
for _, name := range order {
|
||||||
f := thumbnail.Lookup(name)
|
f := thumbnail.Lookup(name)
|
||||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||||
|
|||||||
Reference in New Issue
Block a user