Compare commits

...

7 Commits

Author SHA1 Message Date
2ce29d62ec
update README 2024-05-09 15:59:04 +02:00
84863e72d2
Merge branch '34-spacexhtml-bug-in-split-page-mode' 2024-05-09 15:51:45 +02:00
87faac877c
add option to not preserve aspect on split double page 2024-05-09 15:14:54 +02:00
62fc520ebb
cut more each side of doublepage split in portrait only 2024-05-09 12:28:48 +02:00
27d617640a
detect empty page 2024-05-08 18:49:01 +02:00
f9ebe71b9e
crop double page after autocrop
fix landscape display when each side can be cut differently.
A doublepage is 1 big image, and should be cut as one.
2024-05-07 08:55:42 +02:00
f7f8680eb3
fix doublePage issue when part crop are also a double page 2024-05-06 17:24:31 +02:00
7 changed files with 123 additions and 81 deletions

View File

@ -148,7 +148,7 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : true Auto contrast : true
Auto rotate : true Auto rotate : true
Auto split double page : true Auto split double page : true
@ -196,7 +196,7 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : true Auto contrast : true
Auto rotate : true Auto rotate : true
Auto split double page : true Auto split double page : true
@ -254,7 +254,7 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : false Auto contrast : false
Auto rotate : false Auto rotate : false
Auto split double page : false Auto split double page : false
@ -285,11 +285,12 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : true Auto contrast : true
Auto rotate : true Auto rotate : true
Auto split double page : true Auto split double page : true
Keep double page if split : true Keep double page if split : true
Keep split double page aspect : true
No blank image : true No blank image : true
Manga : true Manga : true
Has cover : true Has cover : true
@ -318,15 +319,13 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : true Auto contrast : false
Auto rotate : true Auto rotate : false
Auto split double page : true Auto split double page : false
Keep double page if split : true
No blank image : true No blank image : true
Manga : false Manga : false
Has cover : true Has cover : true
Limit : 200 Mb
Strip first directory from toc : false Strip first directory from toc : false
Sort path mode : path=alphanumeric, file=alpha Sort path mode : path=alphanumeric, file=alpha
Foreground color : #000 Foreground color : #000
@ -354,7 +353,7 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : false Auto contrast : false
Auto rotate : false Auto rotate : false
Auto split double page : false Auto split double page : false
@ -389,13 +388,14 @@ Options:
Grayscale : true Grayscale : true
Grayscale mode : normal Grayscale mode : normal
Crop : true Crop : true
Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 10% - Skip true Crop ratio : 1 Left - 1 Up - 1 Right - 3 Bottom - Limit 0% - Skip false
Auto contrast : false Auto contrast : false
Auto rotate : false Auto rotate : false
Auto split double page : false Auto split double page : false
No blank image : true No blank image : true
Manga : true Manga : true
Has cover : true Has cover : true
Limit : 200 Mb
Strip first directory from toc : false Strip first directory from toc : false
Sort path mode : path=alphanumeric, file=alpha Sort path mode : path=alphanumeric, file=alpha
Foreground color : #000 Foreground color : #000
@ -482,9 +482,9 @@ Config:
Crop ratio right: ratio of pixels allow to be non blank while cutting on the right. Crop ratio right: ratio of pixels allow to be non blank while cutting on the right.
-crop-ratio-bottom int (default 3) -crop-ratio-bottom int (default 3)
Crop ratio bottom: ratio of pixels allow to be non blank while cutting on the bottom. Crop ratio bottom: ratio of pixels allow to be non blank while cutting on the bottom.
-crop-limit int (default 10) -crop-limit int
Crop limit: maximum number of cropping in percentage allowed. 0 mean unlimited. Crop limit: maximum number of cropping in percentage allowed. 0 mean unlimited.
-crop-skip-if-limit-reached (default true) -crop-skip-if-limit-reached
Crop skip if limit reached. Crop skip if limit reached.
-brightness int -brightness int
Brightness readjustment: between -100 and 100, > 0 lighter, < 0 darker Brightness readjustment: between -100 and 100, > 0 lighter, < 0 darker
@ -498,6 +498,8 @@ Config:
Auto Split double page when width > height Auto Split double page when width > height
-keepdoublepageifsplit (default true) -keepdoublepageifsplit (default true)
Keep the double page if split Keep the double page if split
-keepsplitdoublepageaspect (default true)
Keep aspect of split part of a double page (best for landscape rendering)
-noblankimage (default true) -noblankimage (default true)
Remove blank image Remove blank image
-manga -manga

View File

@ -122,6 +122,7 @@ func (c *Converter) InitParse() {
c.AddBoolParam(&c.Options.AutoRotate, "autorotate", c.Options.AutoRotate, "Auto Rotate page when width > height") c.AddBoolParam(&c.Options.AutoRotate, "autorotate", c.Options.AutoRotate, "Auto Rotate page when width > height")
c.AddBoolParam(&c.Options.AutoSplitDoublePage, "autosplitdoublepage", c.Options.AutoSplitDoublePage, "Auto Split double page when width > height") c.AddBoolParam(&c.Options.AutoSplitDoublePage, "autosplitdoublepage", c.Options.AutoSplitDoublePage, "Auto Split double page when width > height")
c.AddBoolParam(&c.Options.KeepDoublePageIfSplit, "keepdoublepageifsplit", c.Options.KeepDoublePageIfSplit, "Keep the double page if split") c.AddBoolParam(&c.Options.KeepDoublePageIfSplit, "keepdoublepageifsplit", c.Options.KeepDoublePageIfSplit, "Keep the double page if split")
c.AddBoolParam(&c.Options.KeepSplitDoublePageAspect, "keepsplitdoublepageaspect", c.Options.KeepSplitDoublePageAspect, "Keep aspect of split part of a double page (best for landscape rendering)")
c.AddBoolParam(&c.Options.NoBlankImage, "noblankimage", c.Options.NoBlankImage, "Remove blank image") c.AddBoolParam(&c.Options.NoBlankImage, "noblankimage", c.Options.NoBlankImage, "Remove blank image")
c.AddBoolParam(&c.Options.Manga, "manga", c.Options.Manga, "Manga mode (right to left)") c.AddBoolParam(&c.Options.Manga, "manga", c.Options.Manga, "Manga mode (right to left)")
c.AddBoolParam(&c.Options.HasCover, "hascover", c.Options.HasCover, "Has cover. Indicate if your comic have a cover. The first page will be used as a cover and include after the title.") c.AddBoolParam(&c.Options.HasCover, "hascover", c.Options.HasCover, "Has cover. Indicate if your comic have a cover. The first page will be used as a cover and include after the title.")
@ -274,6 +275,11 @@ func (c *Converter) Parse() {
if c.Options.AppleBookCompatibility { if c.Options.AppleBookCompatibility {
c.Options.AutoSplitDoublePage = true c.Options.AutoSplitDoublePage = true
c.Options.KeepDoublePageIfSplit = false c.Options.KeepDoublePageIfSplit = false
c.Options.KeepSplitDoublePageAspect = true
}
if c.Options.PortraitOnly {
c.Options.KeepSplitDoublePageAspect = false
} }
} }

View File

@ -38,6 +38,7 @@ type Options struct {
AutoRotate bool `yaml:"auto_rotate"` AutoRotate bool `yaml:"auto_rotate"`
AutoSplitDoublePage bool `yaml:"auto_split_double_page"` AutoSplitDoublePage bool `yaml:"auto_split_double_page"`
KeepDoublePageIfSplit bool `yaml:"keep_double_page_if_split"` KeepDoublePageIfSplit bool `yaml:"keep_double_page_if_split"`
KeepSplitDoublePageAspect bool `yaml:"keep_split_double_page_aspect"`
NoBlankImage bool `yaml:"no_blank_image"` NoBlankImage bool `yaml:"no_blank_image"`
Manga bool `yaml:"manga"` Manga bool `yaml:"manga"`
HasCover bool `yaml:"has_cover"` HasCover bool `yaml:"has_cover"`
@ -82,25 +83,24 @@ type Options struct {
// New Initialize default options. // New Initialize default options.
func New() *Options { func New() *Options {
return &Options{ return &Options{
Profile: "SR", Profile: "SR",
Quality: 85, Quality: 85,
Grayscale: true, Grayscale: true,
Crop: true, Crop: true,
CropRatioLeft: 1, CropRatioLeft: 1,
CropRatioUp: 1, CropRatioUp: 1,
CropRatioRight: 1, CropRatioRight: 1,
CropRatioBottom: 3, CropRatioBottom: 3,
CropLimit: 10, NoBlankImage: true,
CropSkipIfLimitReached: true, HasCover: true,
NoBlankImage: true, KeepDoublePageIfSplit: true,
HasCover: true, KeepSplitDoublePageAspect: true,
KeepDoublePageIfSplit: true, SortPathMode: 1,
SortPathMode: 1, ForegroundColor: "000",
ForegroundColor: "000", BackgroundColor: "FFF",
BackgroundColor: "FFF", Format: "jpeg",
Format: "jpeg", TitlePage: 1,
TitlePage: 1, profiles: profiles.New(),
profiles: profiles.New(),
} }
} }
@ -179,6 +179,7 @@ func (o *Options) MarshalJSON() ([]byte, error) {
out["autosplitdoublepage"] = o.AutoSplitDoublePage out["autosplitdoublepage"] = o.AutoSplitDoublePage
if o.AutoSplitDoublePage { if o.AutoSplitDoublePage {
out["keepdoublepageifsplit"] = o.KeepDoublePageIfSplit out["keepdoublepageifsplit"] = o.KeepDoublePageIfSplit
out["keepsplitdoublepageaspect"] = o.KeepSplitDoublePageAspect
} }
} }
if o.LimitMb != 0 { if o.LimitMb != 0 {
@ -285,6 +286,7 @@ func (o *Options) ShowConfig() string {
{"Auto rotate", o.AutoRotate, true}, {"Auto rotate", o.AutoRotate, true},
{"Auto split double page", o.AutoSplitDoublePage, o.PortraitOnly || !o.AppleBookCompatibility}, {"Auto split double page", o.AutoSplitDoublePage, o.PortraitOnly || !o.AppleBookCompatibility},
{"Keep double page if split", o.KeepDoublePageIfSplit, (o.PortraitOnly || !o.AppleBookCompatibility) && o.AutoSplitDoublePage}, {"Keep double page if split", o.KeepDoublePageIfSplit, (o.PortraitOnly || !o.AppleBookCompatibility) && o.AutoSplitDoublePage},
{"Keep split double page aspect", o.KeepSplitDoublePageAspect, (o.PortraitOnly || !o.AppleBookCompatibility) && o.AutoSplitDoublePage},
{"No blank image", o.NoBlankImage, true}, {"No blank image", o.NoBlankImage, true},
{"Manga", o.Manga, true}, {"Manga", o.Manga, true},
{"Has cover", o.HasCover, true}, {"Has cover", o.HasCover, true},

View File

@ -28,10 +28,8 @@ type cutRatioOptions struct {
func findMargin(img image.Image, bounds image.Rectangle, cutRatio cutRatioOptions, limit int, skipIfLimitReached bool) image.Rectangle { func findMargin(img image.Image, bounds image.Rectangle, cutRatio cutRatioOptions, limit int, skipIfLimitReached bool) image.Rectangle {
imgArea := bounds imgArea := bounds
maxCropX, maxCropY := imgArea.Dx()*limit/100, imgArea.Dy()*limit/100
LEFT: LEFT:
for x, maxCrop := imgArea.Min.X, maxCropX; x < imgArea.Max.X && (limit == 0 || maxCrop > 0); x, maxCrop = x+1, maxCrop-1 { for x := imgArea.Min.X; x < imgArea.Max.X; x++ {
allowNonBlank := imgArea.Dy() * cutRatio.Left / 100 allowNonBlank := imgArea.Dy() * cutRatio.Left / 100
for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ { for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ {
if !colorIsBlank(img.At(x, y)) { if !colorIsBlank(img.At(x, y)) {
@ -42,13 +40,10 @@ LEFT:
} }
} }
imgArea.Min.X++ imgArea.Min.X++
if limit > 0 && maxCrop == 1 && skipIfLimitReached {
return bounds
}
} }
UP: UP:
for y, maxCrop := imgArea.Min.Y, maxCropY; y < imgArea.Max.Y && (limit == 0 || maxCrop > 0); y, maxCrop = y+1, maxCrop-1 { for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ {
allowNonBlank := imgArea.Dx() * cutRatio.Up / 100 allowNonBlank := imgArea.Dx() * cutRatio.Up / 100
for x := imgArea.Min.X; x < imgArea.Max.X; x++ { for x := imgArea.Min.X; x < imgArea.Max.X; x++ {
if !colorIsBlank(img.At(x, y)) { if !colorIsBlank(img.At(x, y)) {
@ -59,13 +54,10 @@ UP:
} }
} }
imgArea.Min.Y++ imgArea.Min.Y++
if limit > 0 && maxCrop == 1 && skipIfLimitReached {
return bounds
}
} }
RIGHT: RIGHT:
for x, maxCrop := imgArea.Max.X-1, maxCropX; x >= imgArea.Min.X && (limit == 0 || maxCrop > 0); x, maxCrop = x-1, maxCrop-1 { for x := imgArea.Max.X - 1; x >= imgArea.Min.X; x-- {
allowNonBlank := imgArea.Dy() * cutRatio.Right / 100 allowNonBlank := imgArea.Dy() * cutRatio.Right / 100
for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ { for y := imgArea.Min.Y; y < imgArea.Max.Y; y++ {
if !colorIsBlank(img.At(x, y)) { if !colorIsBlank(img.At(x, y)) {
@ -76,13 +68,10 @@ RIGHT:
} }
} }
imgArea.Max.X-- imgArea.Max.X--
if limit > 0 && maxCrop == 1 && skipIfLimitReached {
return bounds
}
} }
BOTTOM: BOTTOM:
for y, maxCrop := imgArea.Max.Y-1, maxCropY; y >= imgArea.Min.Y && (limit == 0 || maxCrop > 0); y, maxCrop = y-1, maxCrop-1 { for y := imgArea.Max.Y - 1; y >= imgArea.Min.Y; y-- {
allowNonBlank := imgArea.Dx() * cutRatio.Bottom / 100 allowNonBlank := imgArea.Dx() * cutRatio.Bottom / 100
for x := imgArea.Min.X; x < imgArea.Max.X; x++ { for x := imgArea.Min.X; x < imgArea.Max.X; x++ {
if !colorIsBlank(img.At(x, y)) { if !colorIsBlank(img.At(x, y)) {
@ -93,10 +82,42 @@ BOTTOM:
} }
} }
imgArea.Max.Y-- imgArea.Max.Y--
if limit > 0 && maxCrop == 1 && skipIfLimitReached {
return bounds
}
} }
// no limit or blankImage
if limit == 0 || imgArea.Dx() == 0 || imgArea.Dy() == 0 {
return imgArea
}
exceedX, exceedY := limitExceed(bounds, imgArea, limit)
if skipIfLimitReached && (exceedX > 0 || exceedY > 0) {
return bounds
}
imgArea.Min.X, imgArea.Max.X = correctLine(imgArea.Min.X, imgArea.Max.X, bounds.Min.X, bounds.Max.X, exceedX)
imgArea.Min.Y, imgArea.Max.Y = correctLine(imgArea.Min.Y, imgArea.Max.Y, bounds.Min.Y, bounds.Max.Y, exceedY)
return imgArea return imgArea
} }
func limitExceed(bounds, newBounds image.Rectangle, limit int) (int, int) {
return bounds.Dx() - newBounds.Dx() - bounds.Dx()*limit/100, bounds.Dy() - newBounds.Dy() - bounds.Dy()*limit/100
}
func correctLine(min, max, bMin, bMax, exceed int) (int, int) {
if exceed <= 0 {
return min, max
}
min -= exceed / 2
max += exceed / 2
if min < bMin {
max += bMin - min
min = bMin
}
if max > bMax {
min -= max - bMax
max = bMax
}
return min, max
}

View File

@ -178,7 +178,9 @@ func (e *EPUBImageProcessor) transformImage(input *task, part int, right bool) *
src := input.Image src := input.Image
srcBounds := src.Bounds() srcBounds := src.Bounds()
if part > 0 { // In portrait only, we don't need to keep aspect ratio between each split.
// We first cut, the crop.
if part > 0 && !e.Image.KeepSplitDoublePageAspect {
g.Add(epubimagefilters.CropSplitDoublePage(right)) g.Add(epubimagefilters.CropSplitDoublePage(right))
} }
@ -205,11 +207,18 @@ func (e *EPUBImageProcessor) transformImage(input *task, part int, right bool) *
} }
} }
// With landscape support, we need to keep aspect ratio between each split
// We first crop, then cut
if part > 0 && e.Image.KeepSplitDoublePageAspect {
g.Add(epubimagefilters.CropSplitDoublePage(right))
}
dstBounds := g.Bounds(src.Bounds()) dstBounds := g.Bounds(src.Bounds())
// Original && Cropped version need to landscape oriented // Original && Cropped version need to landscape oriented
isDoublePage := srcBounds.Dx() > srcBounds.Dy() && dstBounds.Dx() > dstBounds.Dy() // Only part 0 can be a double page
isDoublePage := part == 0 && srcBounds.Dx() > srcBounds.Dy() && dstBounds.Dx() > dstBounds.Dy()
if part == 0 && e.Image.AutoRotate && isDoublePage { if e.Image.AutoRotate && isDoublePage {
g.Add(gift.Rotate90()) g.Add(gift.Rotate90())
} }

View File

@ -22,23 +22,24 @@ type View struct {
} }
type Image struct { type Image struct {
Crop *Crop Crop *Crop
Quality int Quality int
Brightness int Brightness int
Contrast int Contrast int
AutoContrast bool AutoContrast bool
AutoRotate bool AutoRotate bool
AutoSplitDoublePage bool AutoSplitDoublePage bool
KeepDoublePageIfSplit bool KeepDoublePageIfSplit bool
NoBlankImage bool KeepSplitDoublePageAspect bool
Manga bool NoBlankImage bool
HasCover bool Manga bool
View *View HasCover bool
GrayScale bool View *View
GrayScaleMode int GrayScale bool
Resize bool GrayScaleMode int
Format string Resize bool
AppleBookCompatibility bool Format string
AppleBookCompatibility bool
} }
type Options struct { type Options struct {

21
main.go
View File

@ -134,16 +134,17 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s
Limit: cmd.Options.CropLimit, Limit: cmd.Options.CropLimit,
SkipIfLimitReached: cmd.Options.CropSkipIfLimitReached, SkipIfLimitReached: cmd.Options.CropSkipIfLimitReached,
}, },
Quality: cmd.Options.Quality, Quality: cmd.Options.Quality,
Brightness: cmd.Options.Brightness, Brightness: cmd.Options.Brightness,
Contrast: cmd.Options.Contrast, Contrast: cmd.Options.Contrast,
AutoContrast: cmd.Options.AutoContrast, AutoContrast: cmd.Options.AutoContrast,
AutoRotate: cmd.Options.AutoRotate, AutoRotate: cmd.Options.AutoRotate,
AutoSplitDoublePage: cmd.Options.AutoSplitDoublePage, AutoSplitDoublePage: cmd.Options.AutoSplitDoublePage,
KeepDoublePageIfSplit: cmd.Options.KeepDoublePageIfSplit, KeepDoublePageIfSplit: cmd.Options.KeepDoublePageIfSplit,
NoBlankImage: cmd.Options.NoBlankImage, KeepSplitDoublePageAspect: cmd.Options.KeepSplitDoublePageAspect,
Manga: cmd.Options.Manga, NoBlankImage: cmd.Options.NoBlankImage,
HasCover: cmd.Options.HasCover, Manga: cmd.Options.Manga,
HasCover: cmd.Options.HasCover,
View: &epuboptions.View{ View: &epuboptions.View{
Width: profile.Width, Width: profile.Width,
Height: profile.Height, Height: profile.Height,