diff --git a/go.mod b/go.mod
index da65827..329e1ea 100644
--- a/go.mod
+++ b/go.mod
@@ -2,12 +2,15 @@ module go-comic-converter
go 1.19
-require golang.org/x/image v0.2.0
+require (
+ github.com/bmaupin/go-epub v1.0.1
+ github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50
+ golang.org/x/image v0.2.0
+ golang.org/x/mod v0.7.0
+)
require (
- github.com/bmaupin/go-epub v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.3.1 // indirect
github.com/gofrs/uuid v3.1.0+incompatible // indirect
- github.com/vincent-petithory/dataurl v0.0.0-20191104211930-d1553a71de50 // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
)
diff --git a/go.sum b/go.sum
index 904405e..969485f 100644
--- a/go.sum
+++ b/go.sum
@@ -11,7 +11,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
diff --git a/internal/epub/core.go b/internal/epub/core.go
new file mode 100644
index 0000000..1d68eb2
--- /dev/null
+++ b/internal/epub/core.go
@@ -0,0 +1,176 @@
+package epub
+
+import (
+ "archive/zip"
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "sort"
+ "strings"
+ "text/template"
+ "time"
+
+ "github.com/gofrs/uuid"
+)
+
+type Images struct {
+ Id int
+ Path string
+ Name string
+ Title string
+ Data []byte
+ Width int
+ Height int
+}
+
+type EPub struct {
+ Path string
+
+ UID string
+ Title string
+ Author string
+ Publisher string
+ UpdatedAt string
+ ViewWidth int
+ ViewHeight int
+ Quality int
+
+ Images []Images
+ Error error
+}
+
+func NewEpub(path string) *EPub {
+ uid, err := uuid.NewV4()
+ if err != nil {
+ panic(err)
+ }
+ return &EPub{
+ Path: path,
+
+ UID: uid.String(),
+ Title: "Unknown title",
+ Author: "Unknown author",
+ Publisher: "GO Comic Converter",
+ UpdatedAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"),
+ ViewWidth: 0,
+ ViewHeight: 0,
+ Quality: 75,
+
+ Images: make([]Images, 0),
+ }
+}
+
+func (e *EPub) SetTitle(title string) *EPub {
+ e.Title = title
+ return e
+}
+
+func (e *EPub) SetAuthor(author string) *EPub {
+ e.Author = author
+ return e
+}
+
+func (e *EPub) SetSize(w, h int) *EPub {
+ e.ViewWidth = w
+ e.ViewHeight = h
+ return e
+}
+
+func (e *EPub) SetQuality(q int) *EPub {
+ e.Quality = q
+ return e
+}
+
+func (e *EPub) WriteFile(wz *zip.Writer, file, content string) error {
+ m, err := wz.Create(file)
+ if err != nil {
+ return err
+ }
+ _, err = m.Write([]byte(content))
+ return err
+}
+
+func (e *EPub) Render(templateString string, data any) string {
+ tmpl := template.New("parser")
+ tmpl.Funcs(template.FuncMap{"mod": func(i, j int) bool { return i%j == 0 }})
+ tmpl.Funcs(template.FuncMap{"zoom": func(s int, z float32) int { return int(float32(s) * z) }})
+ tmpl, err := tmpl.Parse(templateString)
+ if err != nil {
+ panic(err)
+ }
+ result := &strings.Builder{}
+ if err := tmpl.Execute(result, data); err != nil {
+ panic(err)
+ }
+ return result.String()
+}
+
+func (e *EPub) LoadDir(dirname string) *EPub {
+ err := filepath.WalkDir(dirname, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+ ext := filepath.Ext(path)
+ if strings.ToLower(ext) != ".jpg" {
+ return nil
+ }
+ name := filepath.Base(path)
+ title := name[0 : len(name)-len(ext)]
+
+ e.Images = append(e.Images, Images{
+ Path: path,
+ Name: name,
+ Title: title,
+ })
+ return nil
+ })
+ if err != nil {
+ e.Error = err
+ return e
+ }
+ sort.SliceStable(e.Images, func(i, j int) bool {
+ return strings.Compare(e.Images[i].Path, e.Images[j].Path) < 0
+ })
+
+ for i := range e.Images {
+ e.Images[i].Id = i
+ }
+
+ return e
+}
+
+func (e *EPub) Write() error {
+ if e.Error != nil {
+ return e.Error
+ }
+ w, err := os.Create(e.Path)
+ if err != nil {
+ return err
+ }
+
+ zipContent := [][]string{
+ {"mimetype", TEMPLATE_MIME_TYPE},
+ {"META-INF/container.xml", TEMPLATE_CONTAINER},
+ {"OEBPS/content.opf", e.Render(TEMPLATE_CONTENT, e)},
+ {"OEBPS/toc.ncx", e.Render(TEMPLATE_TOC, e)},
+ {"OEBPS/nav.xhtml", e.Render(TEMPLATE_NAV, e)},
+ {"OEBPS/Text/style.css", TEMPLATE_STYLE},
+ }
+ for _, img := range e.Images {
+ filename := fmt.Sprintf("OEBPS/Text/%d.xhtml", img.Id)
+ zipContent = append(zipContent, []string{filename, e.Render(TEMPLATE_TEXT, img)})
+ }
+
+ wz := zip.NewWriter(w)
+ defer wz.Close()
+ for _, content := range zipContent {
+ if err := e.WriteFile(wz, content[0], content[1]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/internal/epub/templates.go b/internal/epub/templates.go
new file mode 100644
index 0000000..a86f26b
--- /dev/null
+++ b/internal/epub/templates.go
@@ -0,0 +1,24 @@
+package epub
+
+import _ "embed"
+
+//go:embed "templates/mimetype.tmpl"
+var TEMPLATE_MIME_TYPE string
+
+//go:embed "templates/container.xml.tmpl"
+var TEMPLATE_CONTAINER string
+
+//go:embed "templates/content.opf.tmpl"
+var TEMPLATE_CONTENT string
+
+//go:embed "templates/toc.ncx.tmpl"
+var TEMPLATE_TOC string
+
+//go:embed "templates/nav.xhtml.tmpl"
+var TEMPLATE_NAV string
+
+//go:embed "templates/style.css.tmpl"
+var TEMPLATE_STYLE string
+
+//go:embed "templates/text.xhtml.tmpl"
+var TEMPLATE_TEXT string
diff --git a/internal/epub/templates/container.xml.tmpl b/internal/epub/templates/container.xml.tmpl
new file mode 100644
index 0000000..e1d3db9
--- /dev/null
+++ b/internal/epub/templates/container.xml.tmpl
@@ -0,0 +1,6 @@
+
+