mirror of
https://github.com/gen2brain/cbconvert
synced 2026-06-30 09:11:54 +02:00
Add DPI option, issue #52
This commit is contained in:
@@ -39,6 +39,8 @@ type Options struct {
|
||||
Height int
|
||||
// Best fit for required width and height
|
||||
Fit bool
|
||||
// Document rendering resolution in DPI (PDF, EPUB, etc.); 0 uses the default
|
||||
DPI int
|
||||
// 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||
Filter int
|
||||
// Do not convert the cover image
|
||||
@@ -178,6 +180,7 @@ func (o Options) Args() []string {
|
||||
num("width", o.Width, def.Width)
|
||||
num("height", o.Height, def.Height)
|
||||
flag("fit", o.Fit)
|
||||
num("dpi", o.DPI, def.DPI)
|
||||
str("format", o.Format, def.Format)
|
||||
str("archive", o.Archive, def.Archive)
|
||||
num("zip-level", o.ZipLevel, def.ZipLevel)
|
||||
@@ -203,6 +206,15 @@ func (o Options) Args() []string {
|
||||
return args
|
||||
}
|
||||
|
||||
// renderDPI returns the document rendering resolution, falling back to 300 when unset.
|
||||
func (c *Converter) renderDPI() float64 {
|
||||
if c.Opts.DPI > 0 {
|
||||
return float64(c.Opts.DPI)
|
||||
}
|
||||
|
||||
return 300
|
||||
}
|
||||
|
||||
// Cancel cancels the operation.
|
||||
func (c *Converter) Cancel() {
|
||||
if c.OnCancel != nil {
|
||||
|
||||
@@ -48,7 +48,7 @@ func (c *Converter) convertDocument(ctx context.Context, fileName string) error
|
||||
return fmt.Errorf("convertDocument: %w", ctx.Err())
|
||||
}
|
||||
|
||||
img, err := doc.Image(n)
|
||||
img, err := doc.ImageDPI(n, c.renderDPI())
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDocument: %w", err)
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ func (c *Converter) coverDocument(fileName string) (image.Image, error) {
|
||||
}
|
||||
defer doc.Close()
|
||||
|
||||
img, err := doc.Image(0)
|
||||
img, err := doc.ImageDPI(0, c.renderDPI())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverDocument: %w", err)
|
||||
}
|
||||
|
||||
+37
-1
@@ -114,16 +114,52 @@ func TestArgs(t *testing.T) {
|
||||
opts.Effort = 4
|
||||
opts.Lossless = true
|
||||
opts.Width = 1200
|
||||
opts.DPI = 150
|
||||
opts.Grayscale = true
|
||||
opts.OutDir = "/out"
|
||||
|
||||
got := strings.Join(opts.Args(), " ")
|
||||
want := "--width 1200 --format webp --quality 90 --effort 4 --lossless --grayscale --outdir /out"
|
||||
want := "--width 1200 --dpi 150 --format webp --quality 90 --effort 4 --lossless --grayscale --outdir /out"
|
||||
if got != want {
|
||||
t.Errorf("Args() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertDPI(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
dims := func(dpi int) int {
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.DPI = dpi
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.pdf"})
|
||||
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()
|
||||
}
|
||||
|
||||
low := dims(150)
|
||||
high := dims(600)
|
||||
if low >= high {
|
||||
t.Errorf("higher DPI should render larger pages: 150dpi=%d, 600dpi=%d", low, high)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertResize(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
|
||||
@@ -77,6 +77,7 @@ var settings = []setting{
|
||||
{"Suffix", kindStr, ""},
|
||||
{"Width", kindStr, ""},
|
||||
{"Height", kindStr, ""},
|
||||
{"DPI", kindStr, "Default"},
|
||||
{"Size", kindInt, "0"},
|
||||
{"Quality", kindInt, "75"},
|
||||
{"Effort", kindInt, "0"},
|
||||
@@ -203,6 +204,7 @@ func options() cbconvert.Options {
|
||||
opts.Format = strings.ToLower(iup.GetHandle("Format").GetAttribute("VALUESTRING"))
|
||||
opts.Width = iup.GetHandle("Width").GetInt("VALUE")
|
||||
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
||||
opts.DPI = dpiValue(iup.GetHandle("DPI").GetAttribute("VALUE"))
|
||||
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
||||
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
||||
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
||||
@@ -362,6 +364,15 @@ func zipLevel(value string) int {
|
||||
}
|
||||
}
|
||||
|
||||
func dpiValue(value string) int {
|
||||
dpi, err := strconv.Atoi(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return dpi
|
||||
}
|
||||
|
||||
func profileGroup(name string) string {
|
||||
return "Profile:" + name
|
||||
}
|
||||
@@ -846,6 +857,21 @@ func tabs() iup.Ihandle {
|
||||
iup.Text().SetAttributes(`SPIN=YES, SPINMAX=2048, VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Size").
|
||||
SetAttributes(`TIP="Process only files larger than minimum size"`),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label("Document DPI:"),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"EDITBOX": "YES",
|
||||
"VISIBLECOLUMNS": "6",
|
||||
"VALUE": "Default",
|
||||
"1": "Default",
|
||||
"2": "150",
|
||||
"3": "300",
|
||||
"4": "600",
|
||||
"5": "1200",
|
||||
}).SetHandle("DPI").
|
||||
SetAttribute("TIP", "Resolution for rendering documents (PDF, EPUB, etc.); Default is 300"),
|
||||
),
|
||||
iup.Space().SetAttributes("EXPAND=HORIZONTAL"),
|
||||
).SetHandle("VboxInput").SetAttributes("NGAP=10")
|
||||
|
||||
|
||||
@@ -175,6 +175,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
convert.IntVar(&opts.Width, "width", 0, "Image width")
|
||||
convert.IntVar(&opts.Height, "height", 0, "Image height")
|
||||
convert.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||
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.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")
|
||||
@@ -202,6 +203,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
cover.IntVar(&opts.Width, "width", 0, "Image width")
|
||||
cover.IntVar(&opts.Height, "height", 0, "Image height")
|
||||
cover.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||
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.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||
cover.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")
|
||||
@@ -216,6 +218,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
thumbnail.IntVar(&opts.Width, "width", 0, "Image width")
|
||||
thumbnail.IntVar(&opts.Height, "height", 0, "Image height")
|
||||
thumbnail.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||
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.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
|
||||
thumbnail.StringVar(&opts.OutFile, "outfile", "", "Output file")
|
||||
@@ -236,7 +239,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", "zip-level", "quality", "effort", "lossless", "combine", "outfile", "filter", "no-cover", "no-rgb",
|
||||
order := []string{"width", "height", "fit", "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"}
|
||||
for _, name := range order {
|
||||
f := convert.Lookup(name)
|
||||
@@ -244,14 +247,14 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n cover\n \tExtract cover\n\n")
|
||||
order = []string{"width", "height", "fit", "format", "quality", "effort", "lossless", "combine", "outfile", "filter", "outdir", "size", "recursive", "quiet"}
|
||||
order = []string{"width", "height", "fit", "dpi", "format", "quality", "effort", "lossless", "filter", "outdir", "size", "recursive", "quiet"}
|
||||
for _, name := range order {
|
||||
f := cover.Lookup(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, "\n thumbnail\n \tExtract cover thumbnail (freedesktop spec.)\n\n")
|
||||
order = []string{"width", "height", "fit", "filter", "outdir", "outfile", "size", "recursive", "quiet"}
|
||||
order = []string{"width", "height", "fit", "dpi", "filter", "outdir", "outfile", "size", "recursive", "quiet"}
|
||||
for _, name := range order {
|
||||
f := thumbnail.Lookup(name)
|
||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||
|
||||
Reference in New Issue
Block a user