From 1250fa5ea7d4255d4a02b0738fbd42c1aecf5830 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Mon, 26 Dec 2022 19:01:34 +0100 Subject: [PATCH] create epub like kcc --- go.mod | 9 +- go.sum | 3 + internal/epub/core.go | 176 +++++++++++++++++++++ internal/epub/templates.go | 24 +++ internal/epub/templates/container.xml.tmpl | 6 + internal/epub/templates/content.opf.tmpl | 42 +++++ internal/epub/templates/mimetype.tmpl | 1 + internal/epub/templates/nav.xhtml.tmpl | 20 +++ internal/epub/templates/style.css.tmpl | 72 +++++++++ internal/epub/templates/text.xhtml.tmpl | 40 +++++ internal/epub/templates/toc.ncx.tmpl | 14 ++ main.go | 23 ++- 12 files changed, 423 insertions(+), 7 deletions(-) create mode 100644 internal/epub/core.go create mode 100644 internal/epub/templates.go create mode 100644 internal/epub/templates/container.xml.tmpl create mode 100644 internal/epub/templates/content.opf.tmpl create mode 100644 internal/epub/templates/mimetype.tmpl create mode 100644 internal/epub/templates/nav.xhtml.tmpl create mode 100644 internal/epub/templates/style.css.tmpl create mode 100644 internal/epub/templates/text.xhtml.tmpl create mode 100644 internal/epub/templates/toc.ncx.tmpl 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 @@ + + + + + + \ No newline at end of file diff --git a/internal/epub/templates/content.opf.tmpl b/internal/epub/templates/content.opf.tmpl new file mode 100644 index 0000000..a340630 --- /dev/null +++ b/internal/epub/templates/content.opf.tmpl @@ -0,0 +1,42 @@ + + + +{{ .Title }} +en-US +urn:uuid:{{ .UID }} +GO Comic Converter +GO Comic Converter +{{ .UpdatedAt }} + + + + + + + + + + + + + + + +{{ range .Images }} +{{ if eq .Id 0 }} + +{{ end }} + + +{{ end }} + + +{{ range .Images }} +{{ if mod .Id 2 }} + +{{ else }} + +{{ end }} +{{ end }} + + diff --git a/internal/epub/templates/mimetype.tmpl b/internal/epub/templates/mimetype.tmpl new file mode 100644 index 0000000..57ef03f --- /dev/null +++ b/internal/epub/templates/mimetype.tmpl @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/internal/epub/templates/nav.xhtml.tmpl b/internal/epub/templates/nav.xhtml.tmpl new file mode 100644 index 0000000..1a0bec8 --- /dev/null +++ b/internal/epub/templates/nav.xhtml.tmpl @@ -0,0 +1,20 @@ + + + + +{{ .Title }} + + + + + + + \ No newline at end of file diff --git a/internal/epub/templates/style.css.tmpl b/internal/epub/templates/style.css.tmpl new file mode 100644 index 0000000..b8d765a --- /dev/null +++ b/internal/epub/templates/style.css.tmpl @@ -0,0 +1,72 @@ +@page { +margin: 0; +} +body { +display: block; +margin: 0; +padding: 0; +} +#PV { +position: absolute; +width: 100%; +height: 100%; +top: 0; +left: 0; +} +#PV-T { +top: 0; +width: 100%; +height: 50%; +} +#PV-B { +bottom: 0; +width: 100%; +height: 50%; +} +#PV-L { +left: 0; +width: 49.5%; +height: 100%; +float: left; +} +#PV-R { +right: 0; +width: 49.5%; +height: 100%; +float: right; +} +#PV-TL { +top: 0; +left: 0; +width: 49.5%; +height: 50%; +float: left; +} +#PV-TR { +top: 0; +right: 0; +width: 49.5%; +height: 50%; +float: right; +} +#PV-BL { +bottom: 0; +left: 0; +width: 49.5%; +height: 50%; +float: left; +} +#PV-BR { +bottom: 0; +right: 0; +width: 49.5%; +height: 50%; +float: right; +} +.PV-P { +width: 100%; +height: 100%; +top: 0; +position: absolute; +display: none; +} \ No newline at end of file diff --git a/internal/epub/templates/text.xhtml.tmpl b/internal/epub/templates/text.xhtml.tmpl new file mode 100644 index 0000000..ae54f8a --- /dev/null +++ b/internal/epub/templates/text.xhtml.tmpl @@ -0,0 +1,40 @@ + + + + +{{ .Title }} + + + + +
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ + \ No newline at end of file diff --git a/internal/epub/templates/toc.ncx.tmpl b/internal/epub/templates/toc.ncx.tmpl new file mode 100644 index 0000000..179b1be --- /dev/null +++ b/internal/epub/templates/toc.ncx.tmpl @@ -0,0 +1,14 @@ + + + + + + + + + +{{ .Title }} + +{{ .Title }} + + \ No newline at end of file diff --git a/main.go b/main.go index b77717e..a297b29 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "go-comic-converter/internal/epub" imageconverter "go-comic-converter/internal/image-converter" "io/fs" "path/filepath" @@ -10,7 +11,7 @@ import ( "strings" "sync" - "github.com/bmaupin/go-epub" + epub2 "github.com/bmaupin/go-epub" ) type File struct { @@ -21,7 +22,7 @@ type File struct { InternalPath string } -func addImages(doc *epub.Epub, imagesPath []string) { +func addImages(doc *epub2.Epub, imagesPath []string) { wg := &sync.WaitGroup{} todos := make(chan string, runtime.NumCPU()) imageResult := make(chan *File) @@ -95,10 +96,10 @@ func getImages(dirname string) []string { return images } -func main() { +func main2() { imagesPath := getImages("/Users/vincent/Downloads/Bleach T01 (Tite KUBO) [eBook officiel 1920]") - doc := epub.NewEpub("Bleach T01 (Tite KUBO) [eBook officiel 1920]") + doc := epub2.NewEpub("Bleach T01 (Tite KUBO) [eBook officiel 1920]") doc.SetAuthor("Bachelier Vincent") addImages(doc, imagesPath) @@ -108,3 +109,17 @@ func main() { } } + +func main() { + err := epub.NewEpub("/Users/vincent/Downloads/test.epub"). + SetSize(1860, 2480). + SetQuality(75). + SetTitle("Bleach T01 (Tite KUBO) [eBook officiel 1920]"). + SetAuthor("Bachelier Vincent"). + LoadDir("/Users/vincent/Downloads/Bleach T01 (Tite KUBO) [eBook officiel 1920]"). + Write() + + if err != nil { + panic(err) + } +}