From 7adea5ddbd1a9cd38bf207f6e25c3cf711e2408a Mon Sep 17 00:00:00 2001
From: celogeek <65178+celogeek@users.noreply.github.com>
Date: Sat, 14 Jan 2023 18:11:22 +0100
Subject: [PATCH] add auto split double page
---
README.md | 2 +
internal/epub/core.go | 23 ++++----
internal/epub/filters/crop.go | 35 ++++++++++++
internal/epub/image_data.go | 21 +++++--
internal/epub/image_filters.go | 26 +++++++++
internal/epub/image_processing.go | 43 +++++++++-----
internal/epub/templates/content.opf.tmpl | 10 +---
internal/epub/templates/text.xhtml.tmpl | 12 ++--
main.go | 71 +++++++++++++-----------
9 files changed, 167 insertions(+), 76 deletions(-)
create mode 100644 internal/epub/filters/crop.go
diff --git a/README.md b/README.md
index b079c83..d1f051b 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,8 @@ Usage of go-comic-converter:
Author of the epub (default "GO Comic Converter")
-autorotate
Auto Rotate page when width > height
+ -autosplitdoublepage
+ Auto Split double page when width > height
-brightness int
Brightness readjustement: between -100 and 100, > 0 lighter, < 0 darker
-contrast int
diff --git a/internal/epub/core.go b/internal/epub/core.go
index 09602f5..4eb742f 100644
--- a/internal/epub/core.go
+++ b/internal/epub/core.go
@@ -12,16 +12,17 @@ import (
)
type ImageOptions struct {
- Crop bool
- ViewWidth int
- ViewHeight int
- Quality int
- Algo string
- Palette color.Palette
- Brightness int
- Contrast int
- AutoRotate bool
- Workers int
+ Crop bool
+ ViewWidth int
+ ViewHeight int
+ Quality int
+ Algo string
+ Palette color.Palette
+ Brightness int
+ Contrast int
+ AutoRotate bool
+ AutoSplitDoublePage bool
+ Workers int
}
type EpubOptions struct {
@@ -184,7 +185,7 @@ func (e *ePub) Write() error {
wz.WriteImage(part.Cover.Data)
for _, img := range part.Images {
- text := fmt.Sprintf("OEBPS/Text/%d.xhtml", img.Id)
+ text := fmt.Sprintf("OEBPS/Text/%d_p%d.xhtml", img.Id, img.Part)
if err := wz.WriteFile(text, e.render(textTmpl, img)); err != nil {
return err
}
diff --git a/internal/epub/filters/crop.go b/internal/epub/filters/crop.go
new file mode 100644
index 0000000..59fa577
--- /dev/null
+++ b/internal/epub/filters/crop.go
@@ -0,0 +1,35 @@
+package filters
+
+import (
+ "image"
+ "image/draw"
+
+ "github.com/disintegration/gift"
+)
+
+func CropSplitDoublePage(right bool) *cropSplitDoublePage {
+ return &cropSplitDoublePage{right}
+}
+
+type cropSplitDoublePage struct {
+ right bool
+}
+
+func (p *cropSplitDoublePage) Bounds(srcBounds image.Rectangle) (dstBounds image.Rectangle) {
+ if p.right {
+ dstBounds = image.Rectangle{
+ Min: image.Point{srcBounds.Max.X / 2, srcBounds.Min.Y},
+ Max: srcBounds.Max,
+ }
+ } else {
+ dstBounds = image.Rectangle{
+ Min: srcBounds.Min,
+ Max: image.Point{srcBounds.Max.X / 2, srcBounds.Max.Y},
+ }
+ }
+ return
+}
+
+func (p *cropSplitDoublePage) Draw(dst draw.Image, src image.Image, options *gift.Options) {
+ gift.Crop(dst.Bounds()).Draw(dst, src, options)
+}
diff --git a/internal/epub/image_data.go b/internal/epub/image_data.go
index a07a707..be8226b 100644
--- a/internal/epub/image_data.go
+++ b/internal/epub/image_data.go
@@ -4,7 +4,10 @@ import (
"archive/zip"
"bytes"
"compress/flate"
+ "fmt"
"hash/crc32"
+ "image"
+ "image/jpeg"
"time"
)
@@ -17,13 +20,23 @@ func (img *ImageData) CompressedSize() uint64 {
return img.Header.CompressedSize64 + 30 + uint64(len(img.Header.Name))
}
-func newImageData(name string, data []byte) *ImageData {
+func newImageData(id int, part int, img image.Image, quality int) *ImageData {
+ name := fmt.Sprintf("OEBPS/Images/%d_p%d.jpg", id, part)
+ if id == 0 {
+ name = "OEBPS/Images/cover.jpg"
+ }
+
+ data := bytes.NewBuffer([]byte{})
+ if err := jpeg.Encode(data, img, &jpeg.Options{Quality: quality}); err != nil {
+ panic(err)
+ }
+
cdata := bytes.NewBuffer([]byte{})
wcdata, err := flate.NewWriter(cdata, flate.BestCompression)
if err != nil {
panic(err)
}
- wcdata.Write(data)
+ wcdata.Write(data.Bytes())
wcdata.Close()
if err != nil {
panic(err)
@@ -33,8 +46,8 @@ func newImageData(name string, data []byte) *ImageData {
&zip.FileHeader{
Name: name,
CompressedSize64: uint64(cdata.Len()),
- UncompressedSize64: uint64(len(data)),
- CRC32: crc32.Checksum(data, crc32.IEEETable),
+ UncompressedSize64: uint64(data.Len()),
+ CRC32: crc32.Checksum(data.Bytes(), crc32.IEEETable),
Method: zip.Deflate,
ModifiedTime: uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11),
ModifiedDate: uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9),
diff --git a/internal/epub/image_filters.go b/internal/epub/image_filters.go
index 5345c77..c2ec6e5 100644
--- a/internal/epub/image_filters.go
+++ b/internal/epub/image_filters.go
@@ -23,3 +23,29 @@ func NewGift(options *ImageOptions) *gift.GIFT {
)
return g
}
+
+func NewGiftSplitDoublePage(options *ImageOptions) []*gift.GIFT {
+ gifts := make([]*gift.GIFT, 2)
+
+ gifts[0] = gift.New(
+ filters.CropSplitDoublePage(false),
+ )
+
+ gifts[1] = gift.New(
+ filters.CropSplitDoublePage(true),
+ )
+
+ for _, g := range gifts {
+ if options.Contrast != 0 {
+ g.Add(gift.Contrast(float32(options.Contrast)))
+ }
+ if options.Brightness != 0 {
+ g.Add(gift.Brightness(float32(options.Brightness)))
+ }
+ g.Add(
+ gift.ResizeToFit(options.ViewWidth, options.ViewHeight, gift.LanczosResampling),
+ )
+ }
+
+ return gifts
+}
diff --git a/internal/epub/image_processing.go b/internal/epub/image_processing.go
index 8f1d4eb..9421703 100644
--- a/internal/epub/image_processing.go
+++ b/internal/epub/image_processing.go
@@ -6,7 +6,6 @@ import (
"fmt"
"image"
"image/color"
- "image/jpeg"
"io"
"io/fs"
"os"
@@ -24,6 +23,7 @@ import (
type Image struct {
Id int
+ Part int
Data *ImageData
Width int
Height int
@@ -154,23 +154,29 @@ func LoadImages(path string, options *ImageOptions) ([]*Image, error) {
g.Draw(dst, src)
}
- // Encode image
- b := bytes.NewBuffer([]byte{})
- err = jpeg.Encode(b, dst, &jpeg.Options{Quality: options.Quality})
- if err != nil {
- panic(err)
- }
-
- name := fmt.Sprintf("OEBPS/Images/%d.jpg", img.Id)
- if img.Id == 0 {
- name = "OEBPS/Images/cover.jpg"
- }
imageOutput <- &Image{
img.Id,
- newImageData(name, b.Bytes()),
+ 0,
+ newImageData(img.Id, 0, dst, options.Quality),
dst.Bounds().Dx(),
dst.Bounds().Dy(),
}
+
+ if options.AutoSplitDoublePage && src.Bounds().Dx() > src.Bounds().Dy() {
+ gifts := NewGiftSplitDoublePage(options)
+ for i, g := range gifts {
+ part := i + 1
+ dst := image.NewPaletted(g.Bounds(src.Bounds()), options.Palette)
+ g.Draw(dst, src)
+ imageOutput <- &Image{
+ img.Id,
+ part,
+ newImageData(img.Id, part, dst, options.Quality),
+ dst.Bounds().Dx(),
+ dst.Bounds().Dy(),
+ }
+ }
+ }
}
}()
}
@@ -183,7 +189,9 @@ func LoadImages(path string, options *ImageOptions) ([]*Image, error) {
bar := NewBar(imageCount, "Processing", 1, 2)
for image := range imageOutput {
images = append(images, image)
- bar.Add(1)
+ if image.Part == 0 {
+ bar.Add(1)
+ }
}
bar.Close()
@@ -192,7 +200,12 @@ func LoadImages(path string, options *ImageOptions) ([]*Image, error) {
}
sort.Slice(images, func(i, j int) bool {
- return images[i].Id < images[j].Id
+ if images[i].Id < images[j].Id {
+ return true
+ } else if images[i].Id == images[j].Id && images[i].Part < images[j].Part {
+ return true
+ }
+ return false
})
return images, nil
diff --git a/internal/epub/templates/content.opf.tmpl b/internal/epub/templates/content.opf.tmpl
index 0ab3aeb..a212c0b 100644
--- a/internal/epub/templates/content.opf.tmpl
+++ b/internal/epub/templates/content.opf.tmpl
@@ -26,18 +26,14 @@