Merge branch '34-spacexhtml-bug-in-split-page-mode'

This commit is contained in:
Celogeek 2024-05-09 15:51:45 +02:00
commit 84863e72d2
Signed by: celogeek
SSH Key Fingerprint: SHA256:njNJLzoLQdbV9PC6ehcruRb0QnEgxABoCYZ+0+aUIYc
6 changed files with 107 additions and 67 deletions

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,