mirror of
https://github.com/celogeek/go-comic-converter.git
synced 2025-05-25 08:12:36 +02:00
Compare commits
3 Commits
0fdbf5baca
...
1bc85f4c2d
Author | SHA1 | Date | |
---|---|---|---|
1bc85f4c2d | |||
fd796be892 | |||
341fd2649e |
@ -107,7 +107,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.Auto, "auto", false, "Activate all automatic options")
|
c.AddBoolParam(&c.Options.Auto, "auto", false, "Activate all automatic options")
|
||||||
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.NoBlankPage, "noblankpage", c.Options.NoBlankPage, "Remove blank pages")
|
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.")
|
||||||
c.AddIntParam(&c.Options.LimitMb, "limitmb", c.Options.LimitMb, "Limit size of the ePub: Default nolimit (0), Minimum 20")
|
c.AddIntParam(&c.Options.LimitMb, "limitmb", c.Options.LimitMb, "Limit size of the ePub: Default nolimit (0), Minimum 20")
|
||||||
|
@ -33,7 +33,7 @@ type Options struct {
|
|||||||
Auto bool `yaml:"-"`
|
Auto bool `yaml:"-"`
|
||||||
AutoRotate bool `yaml:"auto_rotate"`
|
AutoRotate bool `yaml:"auto_rotate"`
|
||||||
AutoSplitDoublePage bool `yaml:"auto_split_double_page"`
|
AutoSplitDoublePage bool `yaml:"auto_split_double_page"`
|
||||||
NoBlankPage bool `yaml:"no_blank_page"`
|
NoBlankImage bool `yaml:"no_blank_image"`
|
||||||
Manga bool `yaml:"manga"`
|
Manga bool `yaml:"manga"`
|
||||||
HasCover bool `yaml:"has_cover"`
|
HasCover bool `yaml:"has_cover"`
|
||||||
LimitMb int `yaml:"limit_mb"`
|
LimitMb int `yaml:"limit_mb"`
|
||||||
@ -71,7 +71,7 @@ func New() *Options {
|
|||||||
Contrast: 0,
|
Contrast: 0,
|
||||||
AutoRotate: false,
|
AutoRotate: false,
|
||||||
AutoSplitDoublePage: false,
|
AutoSplitDoublePage: false,
|
||||||
NoBlankPage: false,
|
NoBlankImage: true,
|
||||||
Manga: false,
|
Manga: false,
|
||||||
HasCover: true,
|
HasCover: true,
|
||||||
LimitMb: 0,
|
LimitMb: 0,
|
||||||
@ -172,7 +172,7 @@ func (o *Options) ShowConfig() string {
|
|||||||
Contrast : %d
|
Contrast : %d
|
||||||
AutoRotate : %v
|
AutoRotate : %v
|
||||||
AutoSplitDoublePage : %v
|
AutoSplitDoublePage : %v
|
||||||
NoBlankPage : %v
|
NoBlankImage : %v
|
||||||
Manga : %v
|
Manga : %v
|
||||||
HasCover : %v
|
HasCover : %v
|
||||||
LimitMb : %s
|
LimitMb : %s
|
||||||
@ -188,7 +188,7 @@ func (o *Options) ShowConfig() string {
|
|||||||
o.Contrast,
|
o.Contrast,
|
||||||
o.AutoRotate,
|
o.AutoRotate,
|
||||||
o.AutoSplitDoublePage,
|
o.AutoSplitDoublePage,
|
||||||
o.NoBlankPage,
|
o.NoBlankImage,
|
||||||
o.Manga,
|
o.Manga,
|
||||||
o.HasCover,
|
o.HasCover,
|
||||||
limitmb,
|
limitmb,
|
||||||
|
@ -70,7 +70,7 @@ func (e *ePub) render(templateString string, data map[string]any) string {
|
|||||||
// write image to the zip
|
// write image to the zip
|
||||||
func (e *ePub) writeImage(wz *epubzip.EpubZip, img *epubimageprocessor.LoadedImage) error {
|
func (e *ePub) writeImage(wz *epubzip.EpubZip, img *epubimageprocessor.LoadedImage) error {
|
||||||
err := wz.WriteContent(
|
err := wz.WriteContent(
|
||||||
fmt.Sprintf("OEBPS/%s", img.Image.TextPath()),
|
fmt.Sprintf("OEBPS/%s", img.Image.PagePath()),
|
||||||
[]byte(e.render(epubtemplates.Text, map[string]any{
|
[]byte(e.render(epubtemplates.Text, map[string]any{
|
||||||
"Title": fmt.Sprintf("Image %d Part %d", img.Image.Id, img.Image.Part),
|
"Title": fmt.Sprintf("Image %d Part %d", img.Image.Id, img.Image.Part),
|
||||||
"ViewPort": fmt.Sprintf("width=%d,height=%d", e.Image.View.Width, e.Image.View.Height),
|
"ViewPort": fmt.Sprintf("width=%d,height=%d", e.Image.View.Width, e.Image.View.Height),
|
||||||
@ -289,14 +289,14 @@ func (e *ePub) Write() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, img := range part.LoadedImages {
|
lastImage := part.LoadedImages[len(part.LoadedImages)-1]
|
||||||
|
for _, img := range part.LoadedImages {
|
||||||
if err := e.writeImage(wz, img); err != nil {
|
if err := e.writeImage(wz, img); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double Page or Last Image that is not a double page
|
// Double Page or Last Image that is not a double page
|
||||||
if img.Image.DoublePage ||
|
if img.Image.DoublePage || (img.Image.Part == 0 && img == lastImage) {
|
||||||
(img.Image.Part == 0 && i+1 == len(part.LoadedImages)) {
|
|
||||||
if err := e.writeBlank(wz, img.Image); err != nil {
|
if err := e.writeBlank(wz, img.Image); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -15,34 +15,40 @@ type Image struct {
|
|||||||
Width int
|
Width int
|
||||||
Height int
|
Height int
|
||||||
IsCover bool
|
IsCover bool
|
||||||
|
IsBlank bool
|
||||||
DoublePage bool
|
DoublePage bool
|
||||||
Path string
|
Path string
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// key name of the image
|
|
||||||
func (i *Image) Key(prefix string) string {
|
|
||||||
return fmt.Sprintf("%s_%d_p%d", prefix, i.Id, i.Part)
|
|
||||||
}
|
|
||||||
|
|
||||||
// key name of the blank plage after the image
|
// key name of the blank plage after the image
|
||||||
func (i *Image) SpaceKey(prefix string) string {
|
func (i *Image) SpaceKey() string {
|
||||||
return fmt.Sprintf("%s_%d_sp", prefix, i.Id)
|
return fmt.Sprintf("space_%d", i.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// path of the blank page
|
// path of the blank page
|
||||||
func (i *Image) SpacePath() string {
|
func (i *Image) SpacePath() string {
|
||||||
return fmt.Sprintf("Text/%d_sp.xhtml", i.Id)
|
return fmt.Sprintf("Text/%s.xhtml", i.SpaceKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
// text path linked to the image
|
// key for page
|
||||||
func (i *Image) TextPath() string {
|
func (i *Image) PageKey() string {
|
||||||
return fmt.Sprintf("Text/%d_p%d.xhtml", i.Id, i.Part)
|
return fmt.Sprintf("page_%d_p%d", i.Id, i.Part)
|
||||||
|
}
|
||||||
|
|
||||||
|
// page path linked to the image
|
||||||
|
func (i *Image) PagePath() string {
|
||||||
|
return fmt.Sprintf("Text/%s.xhtml", i.PageKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
// key for image
|
||||||
|
func (i *Image) ImgKey() string {
|
||||||
|
return fmt.Sprintf("img_%d_p%d", i.Id, i.Part)
|
||||||
}
|
}
|
||||||
|
|
||||||
// image path
|
// image path
|
||||||
func (i *Image) ImgPath() string {
|
func (i *Image) ImgPath() string {
|
||||||
return fmt.Sprintf("Images/%d_p%d.jpg", i.Id, i.Part)
|
return fmt.Sprintf("Images/%s.jpg", i.ImgKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
// style to apply to the image.
|
// style to apply to the image.
|
||||||
|
@ -27,9 +27,7 @@ func (p *coverTitle) Bounds(srcBounds image.Rectangle) (dstBounds image.Rectangl
|
|||||||
|
|
||||||
// blur the src image, and create a box with the title in the middle
|
// blur the src image, and create a box with the title in the middle
|
||||||
func (p *coverTitle) Draw(dst draw.Image, src image.Image, options *gift.Options) {
|
func (p *coverTitle) Draw(dst draw.Image, src image.Image, options *gift.Options) {
|
||||||
// Create a blur version of the cover
|
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
|
||||||
g := gift.New(gift.GaussianBlur(4))
|
|
||||||
g.Draw(dst, src)
|
|
||||||
|
|
||||||
srcWidth, srcHeight := src.Bounds().Dx(), src.Bounds().Dy()
|
srcWidth, srcHeight := src.Bounds().Dx(), src.Bounds().Dy()
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ func (e *EpubImageProcessor) Load() (LoadedImages, error) {
|
|||||||
Width: dst.Bounds().Dx(),
|
Width: dst.Bounds().Dx(),
|
||||||
Height: dst.Bounds().Dy(),
|
Height: dst.Bounds().Dy(),
|
||||||
IsCover: input.Id == 0 && part == 0,
|
IsCover: input.Id == 0 && part == 0,
|
||||||
|
IsBlank: dst.Bounds().Dx() == 1 && dst.Bounds().Dy() == 1,
|
||||||
DoublePage: part == 0 && src.Bounds().Dx() > src.Bounds().Dy(),
|
DoublePage: part == 0 && src.Bounds().Dx() > src.Bounds().Dy(),
|
||||||
Path: input.Path,
|
Path: input.Path,
|
||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
@ -118,7 +119,7 @@ func (e *EpubImageProcessor) Load() (LoadedImages, error) {
|
|||||||
if output.Image.Part == 0 {
|
if output.Image.Part == 0 {
|
||||||
bar.Add(1)
|
bar.Add(1)
|
||||||
}
|
}
|
||||||
if e.Image.NoBlankPage && output.Image.Width == 1 && output.Image.Height == 1 {
|
if e.Image.NoBlankImage && output.Image.IsBlank {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
images = append(images, output)
|
images = append(images, output)
|
||||||
@ -138,7 +139,8 @@ func (e *EpubImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
var filters, splitFilter []gift.Filter
|
var filters, splitFilter []gift.Filter
|
||||||
var images []image.Image
|
var images []image.Image
|
||||||
|
|
||||||
if e.Image.Crop.Enabled {
|
// Lookup for margin if crop is enable or if we want to remove blank image
|
||||||
|
if e.Image.Crop.Enabled || e.Image.NoBlankImage {
|
||||||
f := epubimagefilters.AutoCrop(
|
f := epubimagefilters.AutoCrop(
|
||||||
src,
|
src,
|
||||||
e.Image.Crop.Left,
|
e.Image.Crop.Left,
|
||||||
@ -146,8 +148,16 @@ func (e *EpubImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
e.Image.Crop.Right,
|
e.Image.Crop.Right,
|
||||||
e.Image.Crop.Bottom,
|
e.Image.Crop.Bottom,
|
||||||
)
|
)
|
||||||
filters = append(filters, f)
|
|
||||||
splitFilter = append(splitFilter, f)
|
// detect if blank image
|
||||||
|
size := f.Bounds(src.Bounds())
|
||||||
|
isBlank := size.Dx() == 0 && size.Dy() == 0
|
||||||
|
|
||||||
|
// crop is enable or if blank image with noblankimage options
|
||||||
|
if e.Image.Crop.Enabled || (e.Image.NoBlankImage && isBlank) {
|
||||||
|
filters = append(filters, f)
|
||||||
|
splitFilter = append(splitFilter, f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Image.AutoRotate && src.Bounds().Dx() > src.Bounds().Dy() {
|
if e.Image.AutoRotate && src.Bounds().Dx() > src.Bounds().Dy() {
|
||||||
|
@ -19,7 +19,7 @@ type Image struct {
|
|||||||
Contrast int
|
Contrast int
|
||||||
AutoRotate bool
|
AutoRotate bool
|
||||||
AutoSplitDoublePage bool
|
AutoSplitDoublePage bool
|
||||||
NoBlankPage bool
|
NoBlankImage bool
|
||||||
Manga bool
|
Manga bool
|
||||||
HasCover bool
|
HasCover bool
|
||||||
View *View
|
View *View
|
||||||
|
@ -109,7 +109,7 @@ func getMeta(o *ContentOptions) []tag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.Cover != nil {
|
if o.Cover != nil {
|
||||||
metas = append(metas, tag{"meta", tagAttrs{"name": "cover", "content": o.Cover.Key("img")}, ""})
|
metas = append(metas, tag{"meta", tagAttrs{"name": "cover", "content": o.Cover.ImgKey()}, ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.Total > 1 {
|
if o.Total > 1 {
|
||||||
@ -124,15 +124,21 @@ func getMeta(o *ContentOptions) []tag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getManifest(o *ContentOptions) []tag {
|
func getManifest(o *ContentOptions) []tag {
|
||||||
itag := func(img *epubimage.Image) tag {
|
var imageTags, pageTags, spaceTags []tag
|
||||||
return tag{"item", tagAttrs{"id": img.Key("img"), "href": img.ImgPath(), "media-type": "image/jpeg"}, ""}
|
addTag := func(img *epubimage.Image, withSpace bool) {
|
||||||
}
|
imageTags = append(imageTags,
|
||||||
htag := func(img *epubimage.Image) tag {
|
tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": "image/jpeg"}, ""},
|
||||||
return tag{"item", tagAttrs{"id": img.Key("page"), "href": img.TextPath(), "media-type": "application/xhtml+xml"}, ""}
|
)
|
||||||
}
|
pageTags = append(pageTags,
|
||||||
stag := func(img *epubimage.Image) tag {
|
tag{"item", tagAttrs{"id": img.PageKey(), "href": img.PagePath(), "media-type": "application/xhtml+xml"}, ""},
|
||||||
return tag{"item", tagAttrs{"id": img.SpaceKey("page"), "href": img.SpacePath(), "media-type": "application/xhtml+xml"}, ""}
|
)
|
||||||
|
if withSpace {
|
||||||
|
spaceTags = append(spaceTags,
|
||||||
|
tag{"item", tagAttrs{"id": img.SpaceKey(), "href": img.SpacePath(), "media-type": "application/xhtml+xml"}, ""},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items := []tag{
|
items := []tag{
|
||||||
{"item", tagAttrs{"id": "toc", "href": "toc.xhtml", "properties": "nav", "media-type": "application/xhtml+xml"}, ""},
|
{"item", tagAttrs{"id": "toc", "href": "toc.xhtml", "properties": "nav", "media-type": "application/xhtml+xml"}, ""},
|
||||||
{"item", tagAttrs{"id": "css", "href": "Text/style.css", "media-type": "text/css"}, ""},
|
{"item", tagAttrs{"id": "css", "href": "Text/style.css", "media-type": "text/css"}, ""},
|
||||||
@ -141,20 +147,18 @@ func getManifest(o *ContentOptions) []tag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if o.ImageOptions.HasCover || o.Current > 1 {
|
if o.ImageOptions.HasCover || o.Current > 1 {
|
||||||
items = append(items, itag(o.Cover), htag(o.Cover))
|
addTag(o.Cover, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, img := range o.Images {
|
|
||||||
if img.Part == 1 {
|
|
||||||
items = append(items, stag(img))
|
|
||||||
}
|
|
||||||
items = append(items, itag(img), htag(img))
|
|
||||||
}
|
|
||||||
lastImage := o.Images[len(o.Images)-1]
|
lastImage := o.Images[len(o.Images)-1]
|
||||||
if lastImage.Part == 0 {
|
for _, img := range o.Images {
|
||||||
items = append(items, stag(lastImage))
|
addTag(img, img.DoublePage || (img.Part == 0 && img == lastImage))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items = append(items, imageTags...)
|
||||||
|
items = append(items, pageTags...)
|
||||||
|
items = append(items, spaceTags...)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,23 +183,23 @@ func getSpine(o *ContentOptions) []tag {
|
|||||||
{"itemref", tagAttrs{"idref": "page_title", "properties": getSpread(true)}, ""},
|
{"itemref", tagAttrs{"idref": "page_title", "properties": getSpread(true)}, ""},
|
||||||
}
|
}
|
||||||
for _, img := range o.Images {
|
for _, img := range o.Images {
|
||||||
spine = append(spine, tag{
|
if img.DoublePage && isOnTheRight {
|
||||||
"itemref",
|
|
||||||
tagAttrs{"idref": img.Key("page"), "properties": getSpread(img.DoublePage && o.ImageOptions.NoBlankPage)},
|
|
||||||
"",
|
|
||||||
})
|
|
||||||
if img.DoublePage && isOnTheRight && !o.ImageOptions.NoBlankPage {
|
|
||||||
spine = append(spine, tag{
|
spine = append(spine, tag{
|
||||||
"itemref",
|
"itemref",
|
||||||
tagAttrs{"idref": img.SpaceKey("page"), "properties": getSpread(false)},
|
tagAttrs{"idref": img.SpaceKey(), "properties": getSpread(false) + " layout-blank"},
|
||||||
"",
|
"",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
spine = append(spine, tag{
|
||||||
|
"itemref",
|
||||||
|
tagAttrs{"idref": img.PageKey(), "properties": getSpread(img.DoublePage)},
|
||||||
|
"",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if o.ImageOptions.Manga == isOnTheRight {
|
if o.ImageOptions.Manga == isOnTheRight {
|
||||||
spine = append(spine, tag{
|
spine = append(spine, tag{
|
||||||
"itemref",
|
"itemref",
|
||||||
tagAttrs{"idref": o.Images[len(o.Images)-1].SpaceKey("page"), "properties": getSpread(false)},
|
tagAttrs{"idref": o.Images[len(o.Images)-1].SpaceKey(), "properties": getSpread(false)},
|
||||||
"",
|
"",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -207,8 +211,8 @@ func getSpine(o *ContentOptions) []tag {
|
|||||||
func getGuide(o *ContentOptions) []tag {
|
func getGuide(o *ContentOptions) []tag {
|
||||||
guide := []tag{}
|
guide := []tag{}
|
||||||
if o.Cover != nil {
|
if o.Cover != nil {
|
||||||
guide = append(guide, tag{"reference", tagAttrs{"type": "cover", "title": "cover", "href": o.Cover.TextPath()}, ""})
|
guide = append(guide, tag{"reference", tagAttrs{"type": "cover", "title": "cover", "href": o.Cover.PagePath()}, ""})
|
||||||
}
|
}
|
||||||
guide = append(guide, tag{"reference", tagAttrs{"type": "text", "title": "content", "href": o.Images[0].TextPath()}, ""})
|
guide = append(guide, tag{"reference", tagAttrs{"type": "text", "title": "content", "href": o.Images[0].PagePath()}, ""})
|
||||||
return guide
|
return guide
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ func Toc(title string, stripFirstDirectoryFromToc bool, images []*epubimage.Imag
|
|||||||
}
|
}
|
||||||
t := paths[parentPath].CreateElement("li")
|
t := paths[parentPath].CreateElement("li")
|
||||||
link := t.CreateElement("a")
|
link := t.CreateElement("a")
|
||||||
link.CreateAttr("href", img.TextPath())
|
link.CreateAttr("href", img.PagePath())
|
||||||
link.CreateText(path)
|
link.CreateText(path)
|
||||||
paths[currentPath] = t.CreateElement("ol")
|
paths[currentPath] = t.CreateElement("ol")
|
||||||
}
|
}
|
||||||
|
2
main.go
2
main.go
@ -126,7 +126,7 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s
|
|||||||
Contrast: cmd.Options.Contrast,
|
Contrast: cmd.Options.Contrast,
|
||||||
AutoRotate: cmd.Options.AutoRotate,
|
AutoRotate: cmd.Options.AutoRotate,
|
||||||
AutoSplitDoublePage: cmd.Options.AutoSplitDoublePage,
|
AutoSplitDoublePage: cmd.Options.AutoSplitDoublePage,
|
||||||
NoBlankPage: cmd.Options.NoBlankPage,
|
NoBlankImage: cmd.Options.NoBlankImage,
|
||||||
Manga: cmd.Options.Manga,
|
Manga: cmd.Options.Manga,
|
||||||
HasCover: cmd.Options.HasCover,
|
HasCover: cmd.Options.HasCover,
|
||||||
View: &epuboptions.View{
|
View: &epuboptions.View{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user