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
|
Height int
|
||||||
// Best fit for required width and height
|
// Best fit for required width and height
|
||||||
Fit bool
|
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
|
// 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
|
||||||
Filter int
|
Filter int
|
||||||
// Do not convert the cover image
|
// Do not convert the cover image
|
||||||
@@ -178,6 +180,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)
|
||||||
|
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)
|
||||||
num("zip-level", o.ZipLevel, def.ZipLevel)
|
num("zip-level", o.ZipLevel, def.ZipLevel)
|
||||||
@@ -203,6 +206,15 @@ func (o Options) Args() []string {
|
|||||||
return args
|
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.
|
// Cancel cancels the operation.
|
||||||
func (c *Converter) Cancel() {
|
func (c *Converter) Cancel() {
|
||||||
if c.OnCancel != nil {
|
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())
|
return fmt.Errorf("convertDocument: %w", ctx.Err())
|
||||||
}
|
}
|
||||||
|
|
||||||
img, err := doc.Image(n)
|
img, err := doc.ImageDPI(n, c.renderDPI())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("convertDocument: %w", err)
|
return fmt.Errorf("convertDocument: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -52,7 +52,7 @@ func (c *Converter) coverDocument(fileName string) (image.Image, error) {
|
|||||||
}
|
}
|
||||||
defer doc.Close()
|
defer doc.Close()
|
||||||
|
|
||||||
img, err := doc.Image(0)
|
img, err := doc.ImageDPI(0, c.renderDPI())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("coverDocument: %w", err)
|
return nil, fmt.Errorf("coverDocument: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-1
@@ -114,16 +114,52 @@ func TestArgs(t *testing.T) {
|
|||||||
opts.Effort = 4
|
opts.Effort = 4
|
||||||
opts.Lossless = true
|
opts.Lossless = true
|
||||||
opts.Width = 1200
|
opts.Width = 1200
|
||||||
|
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 --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 {
|
if got != want {
|
||||||
t.Errorf("Args() = %q, want %q", 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) {
|
func TestConvertResize(t *testing.T) {
|
||||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ var settings = []setting{
|
|||||||
{"Suffix", kindStr, ""},
|
{"Suffix", kindStr, ""},
|
||||||
{"Width", kindStr, ""},
|
{"Width", kindStr, ""},
|
||||||
{"Height", kindStr, ""},
|
{"Height", kindStr, ""},
|
||||||
|
{"DPI", kindStr, "Default"},
|
||||||
{"Size", kindInt, "0"},
|
{"Size", kindInt, "0"},
|
||||||
{"Quality", kindInt, "75"},
|
{"Quality", kindInt, "75"},
|
||||||
{"Effort", kindInt, "0"},
|
{"Effort", kindInt, "0"},
|
||||||
@@ -203,6 +204,7 @@ func options() cbconvert.Options {
|
|||||||
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")
|
||||||
|
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.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")
|
||||||
@@ -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 {
|
func profileGroup(name string) string {
|
||||||
return "Profile:" + name
|
return "Profile:" + name
|
||||||
}
|
}
|
||||||
@@ -846,6 +857,21 @@ func tabs() iup.Ihandle {
|
|||||||
iup.Text().SetAttributes(`SPIN=YES, SPINMAX=2048, VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Size").
|
iup.Text().SetAttributes(`SPIN=YES, SPINMAX=2048, VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Size").
|
||||||
SetAttributes(`TIP="Process only files larger than minimum 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"),
|
iup.Space().SetAttributes("EXPAND=HORIZONTAL"),
|
||||||
).SetHandle("VboxInput").SetAttributes("NGAP=10")
|
).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.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.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")
|
||||||
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.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.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.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")
|
||||||
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")
|
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.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.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")
|
||||||
thumbnail.StringVar(&opts.OutFile, "outfile", "", "Output file")
|
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, "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", "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"}
|
"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)
|
||||||
@@ -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, "%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", "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 {
|
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", "filter", "outdir", "outfile", "size", "recursive", "quiet"}
|
order = []string{"width", "height", "fit", "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