Compare commits

..

No commits in common. "main" and "v3.0.0" have entirely different histories.
main ... v3.0.0

20 changed files with 116 additions and 627 deletions

View File

@ -89,8 +89,6 @@ The extensions can be: `jpg`, `jpeg`, `png`, `webp`, `tiff`.
The case for extensions doesn't matter. The case for extensions doesn't matter.
For the passthrough mode (format=copy), the supported extensions are: `jpg`, `jpeg`, `png`
# Usage # Usage
## Convert directory ## Convert directory
@ -445,34 +443,34 @@ Output:
Config: Config:
-profile string (default "SR") -profile string (default "SR")
Profile to use: Profile to use:
- KoAO - 1404 x 1872 - Kobo Aura ONE - HR ( 2400x3840 ) - High Resolution
- KoF - 1440 x 1920 - Kobo Forma - SR ( 1200x1920 ) - Standard Resolution
- KoE - 1404 x 1872 - Kobo Elipsa - K1 ( 600x670 ) - Kindle 1
- KV - 1072 x 1448 - Kindle Paperwhite 3/4/Voyage/Oasis - K11 ( 1072x1448 ) - Kindle 11
- KoG - 768 x 1024 - Kobo Glo - K2 ( 600x670 ) - Kindle 2
- KoA - 758 x 1024 - Kobo Aura - K34 ( 600x800 ) - Kindle Keyboard/Touch
- RM1 - 1404 x 1872 - reMarkable 1 - K578 ( 600x800 ) - Kindle
- RM2 - 1404 x 1872 - reMarkable 2 - KDX ( 824x1000 ) - Kindle DX/DXG
- K1 - 600 x 670 - Kindle 1 - KPW ( 758x1024 ) - Kindle Paperwhite 1/2
- K11 - 1072 x 1448 - Kindle 11 - KV ( 1072x1448 ) - Kindle Paperwhite 3/4/Voyage/Oasis
- K2 - 600 x 670 - Kindle 2 - KPW5 ( 1236x1648 ) - Kindle Paperwhite 5/Signature Edition
- K34 - 600 x 800 - Kindle Keyboard/Touch - KO ( 1264x1680 ) - Kindle Oasis 2/3
- KPW5 - 1236 x 1648 - Kindle Paperwhite 5/Signature Edition - KS ( 1860x2480 ) - Kindle Scribe
- KoAH2O - 1080 x 1430 - Kobo Aura H2O - KoMT ( 600x800 ) - Kobo Mini/Touch
- KoN - 758 x 1024 - Kobo Nia - KoG ( 768x1024 ) - Kobo Glo
- KoL - 1264 x 1680 - Kobo Libra H2O/Kobo Libra 2 - KoGHD ( 1072x1448 ) - Kobo Glo HD
- HR - 2400 x 3840 - High Resolution - KoA ( 758x1024 ) - Kobo Aura
- KO - 1264 x 1680 - Kindle Oasis 2/3 - KoAHD ( 1080x1440 ) - Kobo Aura HD
- KS - 1860 x 2480 - Kindle Scribe - KoAH2O ( 1080x1430 ) - Kobo Aura H2O
- KoMT - 600 x 800 - Kobo Mini/Touch - KoAO ( 1404x1872 ) - Kobo Aura ONE
- KoAHD - 1080 x 1440 - Kobo Aura HD - KoN ( 758x1024 ) - Kobo Nia
- KoC - 1072 x 1448 - Kobo Clara HD/Kobo Clara 2E - KoC ( 1072x1448 ) - Kobo Clara HD/Kobo Clara 2E
- KoS - 1440 x 1920 - Kobo Sage - KoL ( 1264x1680 ) - Kobo Libra H2O/Kobo Libra 2
- SR - 1200 x 1920 - Standard Resolution - KoF ( 1440x1920 ) - Kobo Forma
- K578 - 600 x 800 - Kindle - KoS ( 1440x1920 ) - Kobo Sage
- KDX - 824 x 1000 - Kindle DX/DXG - KoE ( 1404x1872 ) - Kobo Elipsa
- KPW - 758 x 1024 - Kindle Paperwhite 1/2 - RM1 ( 1404x1872 ) - reMarkable 1
- KoGHD - 1072 x 1448 - Kobo Glo HD - RM2 ( 1404x1872 ) - reMarkable 2
-quality int (default 85) -quality int (default 85)
Quality of the image Quality of the image
-grayscale (default true) -grayscale (default true)
@ -529,10 +527,10 @@ Config:
Foreground color in hexadecimal format RGB. Black=000, White=FFF Foreground color in hexadecimal format RGB. Black=000, White=FFF
-background-color string (default "FFF") -background-color string (default "FFF")
Background color in hexadecimal format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777 Background color in hexadecimal format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777
-resize (default true) -noresize
Reduce image size if exceed device size Do not reduce image size if exceed device size
-format string (default "jpeg") -format string (default "jpeg")
Format of output images: jpeg (lossy), png (lossless), copy (no processing) Format of output images: jpeg (lossy), png (lossless)
-aspect-ratio float -aspect-ratio float
Aspect ratio (height/width) of the output Aspect ratio (height/width) of the output
-1 = same as device -1 = same as device

14
go.mod
View File

@ -3,16 +3,16 @@ module github.com/celogeek/go-comic-converter/v3
go 1.23 go 1.23
require ( require (
github.com/beevik/etree v1.5.0 github.com/beevik/etree v1.4.1
github.com/disintegration/gift v1.2.1 github.com/disintegration/gift v1.2.1
github.com/fogleman/gg v1.3.0 github.com/fogleman/gg v1.3.0
github.com/gofrs/uuid v4.4.0+incompatible github.com/gofrs/uuid v4.4.0+incompatible
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/nwaples/rardecode/v2 v2.1.0 github.com/nwaples/rardecode/v2 v2.0.1
github.com/raff/pdfreader v0.0.0-20220308062436-033e8ac577f0 github.com/raff/pdfreader v0.0.0-20220308062436-033e8ac577f0
github.com/schollz/progressbar/v3 v3.18.0 github.com/schollz/progressbar/v3 v3.17.1
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e
golang.org/x/image v0.24.0 golang.org/x/image v0.23.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
@ -22,7 +22,7 @@ require (
github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
golang.org/x/net v0.35.0 // indirect golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.30.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/term v0.29.0 // indirect golang.org/x/term v0.27.0 // indirect
) )

28
go.sum
View File

@ -1,5 +1,5 @@
github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs= github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI=
github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs= github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@ -24,28 +24,28 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U= github.com/nwaples/rardecode/v2 v2.0.1 h1:3MN6/R+Y4c7e+21U3yhWuUcf72sYmcmr6jtiuAVSH1A=
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= github.com/nwaples/rardecode/v2 v2.0.1/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/raff/pdfreader v0.0.0-20220308062436-033e8ac577f0 h1:fuFvfwIc+cpySYurvDNTs5LIHXP9Cj3reVRplj9Whv4= github.com/raff/pdfreader v0.0.0-20220308062436-033e8ac577f0 h1:fuFvfwIc+cpySYurvDNTs5LIHXP9Cj3reVRplj9Whv4=
github.com/raff/pdfreader v0.0.0-20220308062436-033e8ac577f0/go.mod h1:Ql3QqeGiYGlPOtYz+F/L7J27spqDcdH9LhDHOrrdsD4= github.com/raff/pdfreader v0.0.0-20220308062436-033e8ac577f0/go.mod h1:Ql3QqeGiYGlPOtYz+F/L7J27spqDcdH9LhDHOrrdsD4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA= github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U=
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e h1:IWllFTiDjjLIf2oeKxpIUmtiDV5sn71VgeQgg6vcE7k= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e h1:IWllFTiDjjLIf2oeKxpIUmtiDV5sn71VgeQgg6vcE7k=
github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM= github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e/go.mod h1:d7u6HkTYKSv5m6MCKkOQlHwaShTMl3HjqSGW3XtVhXM=
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -15,7 +15,6 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"runtime" "runtime"
"slices"
"strings" "strings"
"time" "time"
@ -132,7 +131,7 @@ func (c *Converter) InitParse() {
c.AddStringParam(&c.Options.Image.View.Color.Foreground, "foreground-color", c.Options.Image.View.Color.Foreground, "Foreground color in hexadecimal format RGB. Black=000, White=FFF") c.AddStringParam(&c.Options.Image.View.Color.Foreground, "foreground-color", c.Options.Image.View.Color.Foreground, "Foreground color in hexadecimal format RGB. Black=000, White=FFF")
c.AddStringParam(&c.Options.Image.View.Color.Background, "background-color", c.Options.Image.View.Color.Background, "Background color in hexadecimal format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777") c.AddStringParam(&c.Options.Image.View.Color.Background, "background-color", c.Options.Image.View.Color.Background, "Background color in hexadecimal format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777")
c.AddBoolParam(&c.Options.Image.Resize, "resize", c.Options.Image.Resize, "Reduce image size if exceed device size") c.AddBoolParam(&c.Options.Image.Resize, "resize", c.Options.Image.Resize, "Reduce image size if exceed device size")
c.AddStringParam(&c.Options.Image.Format, "format", c.Options.Image.Format, "Format of output images: jpeg (lossy), png (lossless), copy (no processing)") c.AddStringParam(&c.Options.Image.Format, "format", c.Options.Image.Format, "Format of output images: jpeg (lossy), png (lossless)")
c.AddFloatParam(&c.Options.Image.View.AspectRatio, "aspect-ratio", c.Options.Image.View.AspectRatio, "Aspect ratio (height/width) of the output\n -1 = same as device\n 0 = same as source\n1.6 = amazon advice for kindle") c.AddFloatParam(&c.Options.Image.View.AspectRatio, "aspect-ratio", c.Options.Image.View.AspectRatio, "Aspect ratio (height/width) of the output\n -1 = same as device\n 0 = same as source\n1.6 = amazon advice for kindle")
c.AddBoolParam(&c.Options.Image.View.PortraitOnly, "portrait-only", c.Options.Image.View.PortraitOnly, "Portrait only: force orientation to portrait only.") c.AddBoolParam(&c.Options.Image.View.PortraitOnly, "portrait-only", c.Options.Image.View.PortraitOnly, "Portrait only: force orientation to portrait only.")
c.AddIntParam(&c.Options.TitlePage, "titlepage", c.Options.TitlePage, "Title page\n0 = never\n1 = always\n2 = only if epub is split") c.AddIntParam(&c.Options.TitlePage, "titlepage", c.Options.TitlePage, "Title page\n0 = never\n1 = always\n2 = only if epub is split")
@ -380,8 +379,8 @@ func (c *Converter) Validate() error {
} }
// Format // Format
if !slices.Contains([]string{"jpeg", "png", "copy"}, c.Options.Image.Format) { if !(c.Options.Image.Format == "jpeg" || c.Options.Image.Format == "png") {
return errors.New("format should be jpeg, png or copy") return errors.New("format should be jpeg or png")
} }
// Aspect Ratio // Aspect Ratio

View File

@ -9,8 +9,8 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epuboptions"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/utils" "github.com/celogeek/go-comic-converter/v3/internal/pkg/utils"
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
) )
type Options struct { type Options struct {
@ -178,9 +178,9 @@ func (o *Options) ShowConfig() string {
{"Profile", profileDesc, true}, {"Profile", profileDesc, true},
{"Format", o.Image.Format, true}, {"Format", o.Image.Format, true},
{"Quality", o.Image.Quality, o.Image.Format == "jpeg"}, {"Quality", o.Image.Quality, o.Image.Format == "jpeg"},
{"Grayscale", o.Image.GrayScale, o.Image.Format != "copy"}, {"Grayscale", o.Image.GrayScale, true},
{"Grayscale mode", grayscaleMode, o.Image.Format != "copy" && o.Image.GrayScale}, {"Grayscale mode", grayscaleMode, o.Image.GrayScale},
{"Crop", o.Image.Crop.Enabled, o.Image.Format != "copy"}, {"Crop", o.Image.Crop.Enabled, true},
{"Crop ratio", {"Crop ratio",
utils.IntToString(o.Image.Crop.Left) + " Left - " + utils.IntToString(o.Image.Crop.Left) + " Left - " +
utils.IntToString(o.Image.Crop.Up) + " Up - " + utils.IntToString(o.Image.Crop.Up) + " Up - " +
@ -188,15 +188,15 @@ func (o *Options) ShowConfig() string {
utils.IntToString(o.Image.Crop.Bottom) + " Bottom - " + utils.IntToString(o.Image.Crop.Bottom) + " Bottom - " +
"Limit " + utils.IntToString(o.Image.Crop.Limit) + "% - " + "Limit " + utils.IntToString(o.Image.Crop.Limit) + "% - " +
"Skip " + utils.BoolToString(o.Image.Crop.SkipIfLimitReached), "Skip " + utils.BoolToString(o.Image.Crop.SkipIfLimitReached),
o.Image.Format != "copy" && o.Image.Crop.Enabled}, o.Image.Crop.Enabled},
{"Brightness", o.Image.Brightness, o.Image.Format != "copy" && o.Image.Brightness != 0}, {"Brightness", o.Image.Brightness, o.Image.Brightness != 0},
{"Contrast", o.Image.Contrast, o.Image.Format != "copy" && o.Image.Contrast != 0}, {"Contrast", o.Image.Contrast, o.Image.Contrast != 0},
{"Auto contrast", o.Image.AutoContrast, o.Image.Format != "copy"}, {"Auto contrast", o.Image.AutoContrast, true},
{"Auto rotate", o.Image.AutoRotate, o.Image.Format != "copy"}, {"Auto rotate", o.Image.AutoRotate, true},
{"Auto split double page", o.Image.AutoSplitDoublePage, o.Image.Format != "copy" && (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility)}, {"Auto split double page", o.Image.AutoSplitDoublePage, o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility},
{"Keep double page if split", o.Image.KeepDoublePageIfSplit, o.Image.Format != "copy" && (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility) && o.Image.AutoSplitDoublePage}, {"Keep double page if split", o.Image.KeepDoublePageIfSplit, (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility) && o.Image.AutoSplitDoublePage},
{"Keep split double page aspect", o.Image.KeepSplitDoublePageAspect, o.Image.Format != "copy" && (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility) && o.Image.AutoSplitDoublePage}, {"Keep split double page aspect", o.Image.KeepSplitDoublePageAspect, (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility) && o.Image.AutoSplitDoublePage},
{"No blank image", o.Image.NoBlankImage, o.Image.Format != "copy"}, {"No blank image", o.Image.NoBlankImage, true},
{"Manga", o.Image.Manga, true}, {"Manga", o.Image.Manga, true},
{"Has cover", o.Image.HasCover, true}, {"Has cover", o.Image.HasCover, true},
{"Limit", utils.IntToString(o.LimitMb) + " Mb", o.LimitMb != 0}, {"Limit", utils.IntToString(o.LimitMb) + " Mb", o.LimitMb != 0},
@ -204,7 +204,7 @@ func (o *Options) ShowConfig() string {
{"Sort path mode", sortpathmode, true}, {"Sort path mode", sortpathmode, true},
{"Foreground color", "#" + o.Image.View.Color.Foreground, true}, {"Foreground color", "#" + o.Image.View.Color.Foreground, true},
{"Background color", "#" + o.Image.View.Color.Background, true}, {"Background color", "#" + o.Image.View.Color.Background, true},
{"Resize", o.Image.Resize, o.Image.Format != "copy"}, {"Resize", o.Image.Resize, true},
{"Aspect ratio", aspectRatio, true}, {"Aspect ratio", aspectRatio, true},
{"Portrait only", o.Image.View.PortraitOnly, true}, {"Portrait only", o.Image.View.PortraitOnly, true},
{"Title page", titlePage, true}, {"Title page", titlePage, true},

View File

@ -15,21 +15,16 @@ import (
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimagepassthrough"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimageprocessor" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimageprocessor"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epuboptions"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubtemplates" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubtemplates"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubtree" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubtree"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/utils" "github.com/celogeek/go-comic-converter/v3/internal/pkg/utils"
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
) )
type EPUB interface { type EPUB struct {
Write() error
}
type epub struct {
epuboptions.EPUBOptions epuboptions.EPUBOptions
UID string UID string
Publisher string Publisher string
@ -53,25 +48,18 @@ func New(options epuboptions.EPUBOptions) EPUB {
"zoom": func(s int, z float32) int { return int(float32(s) * z) }, "zoom": func(s int, z float32) int { return int(float32(s) * z) },
}) })
var imageProcessor epubimageprocessor.EPUBImageProcessor return EPUB{
if options.Image.Format == "copy" {
imageProcessor = epubimagepassthrough.New(options)
} else {
imageProcessor = epubimageprocessor.New(options)
}
return epub{
EPUBOptions: options, EPUBOptions: options,
UID: uid.String(), UID: uid.String(),
Publisher: "GO Comic Converter", Publisher: "GO Comic Converter",
UpdatedAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"), UpdatedAt: time.Now().UTC().Format("2006-01-02T15:04:05Z"),
templateProcessor: tmpl, templateProcessor: tmpl,
imageProcessor: imageProcessor, imageProcessor: epubimageprocessor.New(options),
} }
} }
// render templates // render templates
func (e epub) render(templateString string, data map[string]any) string { func (e EPUB) render(templateString string, data map[string]any) string {
var result strings.Builder var result strings.Builder
tmpl := template.Must(e.templateProcessor.Parse(templateString)) tmpl := template.Must(e.templateProcessor.Parse(templateString))
if err := tmpl.Execute(&result, data); err != nil { if err := tmpl.Execute(&result, data); err != nil {
@ -81,7 +69,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 epubimage.EPUBImage, zipImg *zip.File) error { func (e EPUB) writeImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, zipImg *zip.File) error {
err := wz.WriteContent( err := wz.WriteContent(
img.EPUBPagePath(), img.EPUBPagePath(),
[]byte(e.render(epubtemplates.Text, map[string]any{ []byte(e.render(epubtemplates.Text, map[string]any{
@ -99,7 +87,7 @@ func (e epub) writeImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, zipImg *zi
} }
// write blank page // write blank page
func (e epub) writeBlank(wz epubzip.EPUBZip, img epubimage.EPUBImage) error { func (e EPUB) writeBlank(wz epubzip.EPUBZip, img epubimage.EPUBImage) error {
return wz.WriteContent( return wz.WriteContent(
img.EPUBSpacePath(), img.EPUBSpacePath(),
[]byte(e.render(epubtemplates.Blank, map[string]any{ []byte(e.render(epubtemplates.Blank, map[string]any{
@ -110,7 +98,7 @@ func (e epub) writeBlank(wz epubzip.EPUBZip, img epubimage.EPUBImage) error {
} }
// write title image // write title image
func (e epub) writeCoverImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, part, totalParts int) error { func (e EPUB) writeCoverImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, part, totalParts int) error {
title := "Cover" title := "Cover"
text := "" text := ""
if totalParts > 1 { if totalParts > 1 {
@ -123,7 +111,7 @@ func (e epub) writeCoverImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, part,
[]byte(e.render(epubtemplates.Text, map[string]any{ []byte(e.render(epubtemplates.Text, map[string]any{
"Title": title, "Title": title,
"ViewPort": e.Image.View.Port(), "ViewPort": e.Image.View.Port(),
"ImagePath": "Images/cover.jpeg", "ImagePath": "Images/cover." + e.Image.Format,
"ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, ""), "ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, ""),
})), })),
); err != nil { ); err != nil {
@ -153,7 +141,7 @@ func (e epub) writeCoverImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, part,
} }
// write title image // write title image
func (e epub) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title string) error { func (e EPUB) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title string) error {
titleAlign := "" titleAlign := ""
if !e.Image.View.PortraitOnly { if !e.Image.View.PortraitOnly {
if e.Image.Manga { if e.Image.Manga {
@ -180,7 +168,7 @@ func (e epub) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title
[]byte(e.render(epubtemplates.Text, map[string]any{ []byte(e.render(epubtemplates.Text, map[string]any{
"Title": title, "Title": title,
"ViewPort": e.Image.View.Port(), "ViewPort": e.Image.View.Port(),
"ImagePath": "Images/title.jpeg", "ImagePath": "Images/title." + e.Image.Format,
"ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, titleAlign), "ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, titleAlign),
})), })),
); err != nil { ); err != nil {
@ -209,7 +197,7 @@ func (e epub) writeTitleImage(wz epubzip.EPUBZip, img epubimage.EPUBImage, title
} }
// extract image and split it into part // extract image and split it into part
func (e epub) getParts() (parts []epubPart, imgStorage epubzip.StorageImageReader, err error) { func (e EPUB) getParts() (parts []epubPart, imgStorage epubzip.StorageImageReader, err error) {
images, err := e.imageProcessor.Load() images, err := e.imageProcessor.Load()
if err != nil { if err != nil {
@ -280,7 +268,7 @@ func (e epub) getParts() (parts []epubPart, imgStorage epubzip.StorageImageReade
// create a tree from the directories. // create a tree from the directories.
// //
// this is used to simulate the toc. // this is used to simulate the toc.
func (e epub) getTree(images []epubimage.EPUBImage, skipFiles bool) string { func (e EPUB) getTree(images []epubimage.EPUBImage, skipFiles bool) string {
t := epubtree.New() t := epubtree.New()
for _, img := range images { for _, img := range images {
if skipFiles { if skipFiles {
@ -297,7 +285,7 @@ func (e epub) getTree(images []epubimage.EPUBImage, skipFiles bool) string {
return c.WriteString("") return c.WriteString("")
} }
func (e epub) computeAspectRatio(epubParts []epubPart) float64 { func (e EPUB) computeAspectRatio(epubParts []epubPart) float64 {
var ( var (
bestAspectRatio float64 bestAspectRatio float64
bestAspectRatioCount int bestAspectRatioCount int
@ -324,7 +312,7 @@ func (e epub) computeAspectRatio(epubParts []epubPart) float64 {
return bestAspectRatio return bestAspectRatio
} }
func (e epub) computeViewPort(epubParts []epubPart) (int, int) { func (e EPUB) computeViewPort(epubParts []epubPart) (int, int) {
if e.Image.View.AspectRatio == -1 { if e.Image.View.AspectRatio == -1 {
//keep device size //keep device size
return e.Image.View.Width, e.Image.View.Height return e.Image.View.Width, e.Image.View.Height
@ -344,7 +332,7 @@ func (e epub) computeViewPort(epubParts []epubPart) (int, int) {
} }
} }
func (e epub) writePart(path string, currentPart, totalParts int, part epubPart, imgStorage epubzip.StorageImageReader) error { func (e EPUB) writePart(path string, currentPart, totalParts int, part epubPart, imgStorage epubzip.StorageImageReader) error {
hasTitlePage := e.TitlePage == 1 || (e.TitlePage == 2 && totalParts > 1) hasTitlePage := e.TitlePage == 1 || (e.TitlePage == 2 && totalParts > 1)
wz, err := epubzip.New(path) wz, err := epubzip.New(path)
@ -425,7 +413,7 @@ func (e epub) writePart(path string, currentPart, totalParts int, part epubPart,
} }
// create the zip // create the zip
func (e epub) Write() error { func (e EPUB) Write() error {
epubParts, imgStorage, err := e.getParts() epubParts, imgStorage, err := e.getParts()
if err != nil { if err != nil {
return err return err

View File

@ -73,11 +73,6 @@ func (i EPUBImage) EPUBImgPath() string {
return "OEBPS/" + i.ImgPath() return "OEBPS/" + i.ImgPath()
} }
// MediaType of the epub image
func (i EPUBImage) MediaType() string {
return "image/" + i.Format
}
// ImgStyle style to apply to the image. // ImgStyle style to apply to the image.
// //
// center by default. // center by default.

View File

@ -1,432 +0,0 @@
package epubimagepassthrough
import (
"archive/zip"
"bytes"
"errors"
"fmt"
"image"
"image/jpeg"
"image/png"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"github.com/nwaples/rardecode/v2"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimageprocessor"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/sortpath"
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
)
type ePUBImagePassthrough struct {
epuboptions.EPUBOptions
}
func (e ePUBImagePassthrough) Load() (images []epubimage.EPUBImage, err error) {
fi, err := os.Stat(e.Input)
if err != nil {
return
}
if fi.IsDir() {
return e.loadDir()
} else {
switch ext := strings.ToLower(filepath.Ext(e.Input)); ext {
case ".cbz", ".zip":
return e.loadCbz()
case ".cbr", ".rar":
return e.loadCbr()
default:
return nil, fmt.Errorf("unknown file format (%s): support .cbz, .zip, .cbr, .rar", ext)
}
}
}
func (e ePUBImagePassthrough) CoverTitleData(o epubimageprocessor.CoverTitleDataOptions) (epubzip.Image, error) {
return epubimageprocessor.New(e.EPUBOptions).CoverTitleData(o)
}
var errNoImagesFound = errors.New("no images found")
func New(o epuboptions.EPUBOptions) epubimageprocessor.EPUBImageProcessor {
return ePUBImagePassthrough{o}
}
func (e ePUBImagePassthrough) loadDir() (images []epubimage.EPUBImage, err error) {
imagesPath := make([]string, 0)
input := filepath.Clean(e.Input)
err = filepath.WalkDir(input, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && e.isSupportedImage(path) {
imagesPath = append(imagesPath, path)
}
return nil
})
if err != nil {
return
}
if len(imagesPath) == 0 {
err = errNoImagesFound
return
}
sort.Sort(sortpath.By(imagesPath, e.SortPathMode))
var imgStorage epubzip.StorageImageWriter
imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format)
if err != nil {
return
}
defer imgStorage.Close()
// processing
bar := epubprogress.New(epubprogress.Options{
Quiet: e.Quiet,
Json: e.Json,
Max: len(imagesPath),
Description: "Copying",
CurrentJob: 1,
TotalJob: 2,
})
defer bar.Close()
for i, imgPath := range imagesPath {
var img epubimage.EPUBImage
img, err = e.copyRawDataToStorage(
imgStorage,
func() ([]byte, error) {
f, err := os.Open(imgPath)
if err != nil {
return nil, err
}
defer f.Close()
return io.ReadAll(f)
},
i,
input,
imgPath,
)
if err != nil {
return
}
images = append(images, img)
_ = bar.Add(1)
}
if len(images) == 0 {
err = errNoImagesFound
}
return
}
func (e ePUBImagePassthrough) loadCbz() (images []epubimage.EPUBImage, err error) {
images = make([]epubimage.EPUBImage, 0)
input := filepath.Clean(e.Input)
r, err := zip.OpenReader(input)
if err != nil {
return
}
defer r.Close()
imagesZip := make([]*zip.File, 0)
for _, f := range r.File {
if !f.FileInfo().IsDir() && e.isSupportedImage(f.Name) {
imagesZip = append(imagesZip, f)
}
}
if len(imagesZip) == 0 {
err = errNoImagesFound
return
}
var names []string
for _, img := range imagesZip {
names = append(names, img.Name)
}
sort.Sort(sortpath.By(names, e.SortPathMode))
indexedNames := make(map[string]int)
for i, name := range names {
indexedNames[name] = i
}
var imgStorage epubzip.StorageImageWriter
imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format)
if err != nil {
return
}
defer imgStorage.Close()
// processing
bar := epubprogress.New(epubprogress.Options{
Quiet: e.Quiet,
Json: e.Json,
Max: len(imagesZip),
Description: "Copying",
CurrentJob: 1,
TotalJob: 2,
})
defer bar.Close()
for _, imgZip := range imagesZip {
if _, ok := indexedNames[imgZip.Name]; !ok {
continue
}
var img epubimage.EPUBImage
img, err = e.copyRawDataToStorage(
imgStorage,
func() ([]byte, error) {
f, err := imgZip.Open()
if err != nil {
return nil, err
}
defer f.Close()
return io.ReadAll(f)
},
indexedNames[imgZip.Name],
"",
imgZip.Name,
)
if err != nil {
return
}
images = append(images, img)
_ = bar.Add(1)
}
if len(images) == 0 {
err = errNoImagesFound
}
return
}
func (e ePUBImagePassthrough) loadCbr() (images []epubimage.EPUBImage, err error) {
images = make([]epubimage.EPUBImage, 0)
var isSolid bool
files, err := rardecode.List(e.Input)
if err != nil {
return
}
names := make([]string, 0)
for _, f := range files {
if !f.IsDir && e.isSupportedImage(f.Name) {
if f.Solid {
isSolid = true
}
names = append(names, f.Name)
}
}
if len(names) == 0 {
err = errNoImagesFound
return
}
sort.Sort(sortpath.By(names, e.SortPathMode))
indexedNames := make(map[string]int)
for i, name := range names {
indexedNames[name] = i
}
var imgStorage epubzip.StorageImageWriter
imgStorage, err = epubzip.NewStorageImageWriter(e.ImgStorage(), e.Image.Format)
if err != nil {
return
}
defer imgStorage.Close()
// processing
bar := epubprogress.New(epubprogress.Options{
Quiet: e.Quiet,
Json: e.Json,
Max: len(names),
Description: "Copying",
CurrentJob: 1,
TotalJob: 2,
})
defer bar.Close()
if isSolid {
var r *rardecode.ReadCloser
r, err = rardecode.OpenReader(e.Input)
if err != nil {
return
}
defer r.Close()
for {
f, rerr := r.Next()
if rerr != nil {
if rerr == io.EOF {
break
}
err = rerr
return
}
if _, ok := indexedNames[f.Name]; !ok {
continue
}
var img epubimage.EPUBImage
img, err = e.copyRawDataToStorage(
imgStorage,
func() ([]byte, error) {
return io.ReadAll(r)
},
indexedNames[f.Name],
"",
f.Name,
)
if err != nil {
return
}
images = append(images, img)
_ = bar.Add(1)
}
} else {
for _, file := range files {
if i, ok := indexedNames[file.Name]; ok {
var img epubimage.EPUBImage
img, err = e.copyRawDataToStorage(
imgStorage,
func() ([]byte, error) {
f, err := file.Open()
if err != nil {
return nil, err
}
defer f.Close()
return io.ReadAll(f)
},
i,
"",
file.Name,
)
if err != nil {
return
}
images = append(images, img)
_ = bar.Add(1)
}
}
}
if len(images) == 0 {
err = errNoImagesFound
}
return
}
func (e ePUBImagePassthrough) isSupportedImage(path string) bool {
switch strings.ToLower(filepath.Ext(path)) {
case ".jpg", ".jpeg", ".png":
{
return !strings.HasPrefix(filepath.Base(path), ".")
}
}
return false
}
func (e ePUBImagePassthrough) copyRawDataToStorage(
imgStorage epubzip.StorageImageWriter,
getData func() ([]byte, error),
id int,
dirname string,
filename string,
) (img epubimage.EPUBImage, err error) {
var uncompressedData []byte
uncompressedData, err = getData()
if err != nil {
return
}
p, fn := filepath.Split(filepath.Clean(filename))
if p == dirname {
p = ""
} else {
p = p[len(dirname)+1:]
}
var (
format string
decodeConfig func(r io.Reader) (image.Config, error)
decode func(r io.Reader) (image.Image, error)
)
switch filepath.Ext(fn) {
case ".png":
format = "png"
decodeConfig = png.DecodeConfig
decode = png.Decode
case ".jpg", ".jpeg":
format = "jpeg"
decodeConfig = jpeg.DecodeConfig
decode = jpeg.Decode
}
var config image.Config
config, err = decodeConfig(bytes.NewReader(uncompressedData))
if err != nil {
return
}
var rawImage image.Image
if id == 0 {
rawImage, err = decode(bytes.NewReader(uncompressedData))
if err != nil {
return
}
}
img = epubimage.EPUBImage{
Id: id,
Part: 0,
Raw: rawImage,
Width: config.Width,
Height: config.Height,
IsBlank: false,
DoublePage: config.Width > config.Height,
Path: p,
Name: fn,
Format: format,
OriginalAspectRatio: float64(config.Height) / float64(config.Width),
}
err = imgStorage.AddRaw(img.EPUBImgPath(), uncompressedData)
return
}

View File

@ -43,18 +43,18 @@ type task struct {
var errNoImagesFound = errors.New("no images found") var errNoImagesFound = errors.New("no images found")
// only accept jpg, png and webp as source file // only accept jpg, png and webp as source file
func (e ePUBImageProcessor) isSupportedImage(path string) bool { func (e EPUBImageProcessor) isSupportedImage(path string) bool {
switch strings.ToLower(filepath.Ext(path)) { switch strings.ToLower(filepath.Ext(path)) {
case ".jpg", ".jpeg", ".png", ".webp", ".tiff": case ".jpg", ".jpeg", ".png", ".webp", ".tiff":
{ {
return !strings.HasPrefix(filepath.Base(path), ".") return true
} }
} }
return false return false
} }
// load images from input // Load images from input
func (e ePUBImageProcessor) load() (totalImages int, output chan task, err error) { func (e EPUBImageProcessor) load() (totalImages int, output chan task, err error) {
fi, err := os.Stat(e.Input) fi, err := os.Stat(e.Input)
if err != nil { if err != nil {
return return
@ -78,7 +78,7 @@ func (e ePUBImageProcessor) load() (totalImages int, output chan task, err error
} }
} }
func (e ePUBImageProcessor) corruptedImage(path, name string) image.Image { func (e EPUBImageProcessor) corruptedImage(path, name string) image.Image {
var w, h float64 = 1200, 1920 var w, h float64 = 1200, 1920
f, _ := truetype.Parse(gomonobold.TTF) f, _ := truetype.Parse(gomonobold.TTF)
face := truetype.NewFace(f, &truetype.Options{Size: 64, DPI: 72}) face := truetype.NewFace(f, &truetype.Options{Size: 64, DPI: 72})
@ -102,7 +102,7 @@ func (e ePUBImageProcessor) corruptedImage(path, name string) image.Image {
} }
// load a directory of images // load a directory of images
func (e ePUBImageProcessor) loadDir() (totalImages int, output chan task, err error) { func (e EPUBImageProcessor) loadDir() (totalImages int, output chan task, err error) {
images := make([]string, 0) images := make([]string, 0)
input := filepath.Clean(e.Input) input := filepath.Clean(e.Input)
@ -191,7 +191,7 @@ func (e ePUBImageProcessor) loadDir() (totalImages int, output chan task, err er
} }
// load a zip file that include images // load a zip file that include images
func (e ePUBImageProcessor) loadCbz() (totalImages int, output chan task, err error) { func (e EPUBImageProcessor) loadCbz() (totalImages int, output chan task, err error) {
r, err := zip.OpenReader(e.Input) r, err := zip.OpenReader(e.Input)
if err != nil { if err != nil {
return return
@ -277,7 +277,7 @@ func (e ePUBImageProcessor) loadCbz() (totalImages int, output chan task, err er
} }
// load a rar file that include images // load a rar file that include images
func (e ePUBImageProcessor) loadCbr() (totalImages int, output chan task, err error) { func (e EPUBImageProcessor) loadCbr() (totalImages int, output chan task, err error) {
var isSolid bool var isSolid bool
files, err := rardecode.List(e.Input) files, err := rardecode.List(e.Input)
if err != nil { if err != nil {
@ -393,7 +393,7 @@ func (e ePUBImageProcessor) loadCbr() (totalImages int, output chan task, err er
} }
// extract image from a pdf // extract image from a pdf
func (e ePUBImageProcessor) loadPdf() (totalImages int, output chan task, err error) { func (e EPUBImageProcessor) loadPdf() (totalImages int, output chan task, err error) {
pdf := pdfread.Load(e.Input) pdf := pdfread.Load(e.Input)
if pdf == nil { if pdf == nil {
err = fmt.Errorf("can't read pdf") err = fmt.Errorf("can't read pdf")

View File

@ -11,27 +11,22 @@ import (
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimagefilters" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimagefilters"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epuboptions"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubprogress"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubzip"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/utils" "github.com/celogeek/go-comic-converter/v3/internal/pkg/utils"
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
) )
type EPUBImageProcessor interface { type EPUBImageProcessor struct {
Load() (images []epubimage.EPUBImage, err error)
CoverTitleData(o CoverTitleDataOptions) (epubzip.Image, error)
}
type ePUBImageProcessor struct {
epuboptions.EPUBOptions epuboptions.EPUBOptions
} }
func New(o epuboptions.EPUBOptions) EPUBImageProcessor { func New(o epuboptions.EPUBOptions) EPUBImageProcessor {
return ePUBImageProcessor{o} return EPUBImageProcessor{o}
} }
// Load extract and convert images // Load extract and convert images
func (e ePUBImageProcessor) Load() (images []epubimage.EPUBImage, err error) { func (e EPUBImageProcessor) Load() (images []epubimage.EPUBImage, err error) {
images = make([]epubimage.EPUBImage, 0) images = make([]epubimage.EPUBImage, 0)
imageCount, imageInput, err := e.load() imageCount, imageInput, err := e.load()
if err != nil { if err != nil {
@ -141,7 +136,7 @@ func (e ePUBImageProcessor) Load() (images []epubimage.EPUBImage, err error) {
return images, nil return images, nil
} }
func (e ePUBImageProcessor) createImage(src image.Image, r image.Rectangle) draw.Image { func (e EPUBImageProcessor) createImage(src image.Image, r image.Rectangle) draw.Image {
if e.EPUBOptions.Image.GrayScale { if e.EPUBOptions.Image.GrayScale {
return image.NewGray(r) return image.NewGray(r)
} }
@ -174,7 +169,7 @@ func (e ePUBImageProcessor) createImage(src image.Image, r image.Rectangle) draw
// transform image into 1 or 3 images // transform image into 1 or 3 images
// only doublepage with autosplit has 3 versions // only doublepage with autosplit has 3 versions
func (e ePUBImageProcessor) transformImage(input task, part int, right bool) epubimage.EPUBImage { func (e EPUBImageProcessor) transformImage(input task, part int, right bool) epubimage.EPUBImage {
g := gift.New() g := gift.New()
src := input.Image src := input.Image
srcBounds := src.Bounds() srcBounds := src.Bounds()
@ -291,7 +286,7 @@ type CoverTitleDataOptions struct {
BorderSize int BorderSize int
} }
func (e ePUBImageProcessor) cover16LevelOfGray(bounds image.Rectangle) draw.Image { func (e EPUBImageProcessor) Cover16LevelOfGray(bounds image.Rectangle) draw.Image {
return image.NewPaletted(bounds, color.Palette{ return image.NewPaletted(bounds, color.Palette{
color.Gray{}, color.Gray{},
color.Gray{Y: 0x11}, color.Gray{Y: 0x11},
@ -313,20 +308,20 @@ func (e ePUBImageProcessor) cover16LevelOfGray(bounds image.Rectangle) draw.Imag
} }
// CoverTitleData create a title page with the cover // CoverTitleData create a title page with the cover
func (e ePUBImageProcessor) CoverTitleData(o CoverTitleDataOptions) (epubzip.Image, error) { func (e EPUBImageProcessor) CoverTitleData(o CoverTitleDataOptions) (epubzip.Image, error) {
// Create a blur version of the cover // Create a blur version of the cover
g := gift.New(epubimagefilters.CoverTitle(o.Text, o.Align, o.PctWidth, o.PctMargin, o.MaxFontSize, o.BorderSize)) g := gift.New(epubimagefilters.CoverTitle(o.Text, o.Align, o.PctWidth, o.PctMargin, o.MaxFontSize, o.BorderSize))
var dst draw.Image var dst draw.Image
if o.Name == "cover" && e.Image.GrayScale { if o.Name == "cover" && e.Image.GrayScale {
dst = e.cover16LevelOfGray(o.Src.Bounds()) dst = e.Cover16LevelOfGray(o.Src.Bounds())
} else { } else {
dst = e.createImage(o.Src, g.Bounds(o.Src.Bounds())) dst = e.createImage(o.Src, g.Bounds(o.Src.Bounds()))
} }
g.Draw(dst, o.Src) g.Draw(dst, o.Src)
return epubzip.CompressImage( return epubzip.CompressImage(
"OEBPS/Images/"+o.Name+".jpeg", "OEBPS/Images/"+o.Name+"."+e.Image.Format,
"jpeg", e.Image.Format,
dst, dst,
e.Image.Quality, e.Image.Quality,
) )

View File

@ -20,3 +20,7 @@ type Image struct {
Format string `yaml:"format" json:"format"` Format string `yaml:"format" json:"format"`
AppleBookCompatibility bool `yaml:"apple_book_compatibility" json:"apple_book_compatibility"` AppleBookCompatibility bool `yaml:"apple_book_compatibility" json:"apple_book_compatibility"`
} }
func (i Image) MediaType() string {
return "image/" + i.Format
}

View File

@ -4,8 +4,8 @@ import (
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage" "github.com/celogeek/go-comic-converter/v3/internal/pkg/epubimage"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epuboptions"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/utils" "github.com/celogeek/go-comic-converter/v3/internal/pkg/utils"
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
) )
type Content struct { type Content struct {
@ -144,7 +144,7 @@ func (o Content) getManifest() []tag {
var imageTags, pageTags, spaceTags []tag var imageTags, pageTags, spaceTags []tag
addTag := func(img epubimage.EPUBImage, withSpace bool) { addTag := func(img epubimage.EPUBImage, withSpace bool) {
imageTags = append(imageTags, imageTags = append(imageTags,
tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": img.MediaType()}, ""}, tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": o.ImageOptions.MediaType()}, ""},
) )
pageTags = append(pageTags, pageTags = append(pageTags,
tag{"item", tagAttrs{"id": img.PageKey(), "href": img.PagePath(), "media-type": "application/xhtml+xml"}, ""}, tag{"item", tagAttrs{"id": img.PageKey(), "href": img.PagePath(), "media-type": "application/xhtml+xml"}, ""},
@ -160,13 +160,13 @@ func (o Content) getManifest() []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"}, ""},
{"item", tagAttrs{"id": "page_cover", "href": "Text/cover.xhtml", "media-type": "application/xhtml+xml"}, ""}, {"item", tagAttrs{"id": "page_cover", "href": "Text/cover.xhtml", "media-type": "application/xhtml+xml"}, ""},
{"item", tagAttrs{"id": "img_cover", "href": "Images/cover.jpeg", "media-type": "image/jpeg"}, ""}, {"item", tagAttrs{"id": "img_cover", "href": "Images/cover." + o.ImageOptions.Format, "media-type": o.ImageOptions.MediaType()}, ""},
} }
if o.HasTitlePage { if o.HasTitlePage {
items = append(items, items = append(items,
tag{"item", tagAttrs{"id": "page_title", "href": "Text/title.xhtml", "media-type": "application/xhtml+xml"}, ""}, tag{"item", tagAttrs{"id": "page_title", "href": "Text/title.xhtml", "media-type": "application/xhtml+xml"}, ""},
tag{"item", tagAttrs{"id": "img_title", "href": "Images/title.jpeg", "media-type": "image/jpeg"}, ""}, tag{"item", tagAttrs{"id": "img_title", "href": "Images/title." + o.ImageOptions.Format, "media-type": o.ImageOptions.MediaType()}, ""},
) )
if !o.ImageOptions.View.PortraitOnly { if !o.ImageOptions.View.PortraitOnly {

View File

@ -15,5 +15,4 @@ img {
margin:0; margin:0;
padding:0; padding:0;
z-index:0; z-index:0;
object-fit: contain; }
}

View File

@ -66,39 +66,3 @@ func CompressImage(filename string, format string, img image.Image, quality int)
cdata.Bytes(), cdata.Bytes(),
}, nil }, nil
} }
func CompressRaw(filename string, uncompressedData []byte) (Image, error) {
var (
cdata bytes.Buffer
err error
)
wcdata, err := flate.NewWriter(&cdata, flate.BestCompression)
if err != nil {
return Image{}, err
}
_, err = wcdata.Write(uncompressedData)
if err != nil {
return Image{}, err
}
err = wcdata.Close()
if err != nil {
return Image{}, err
}
t := time.Now()
//goland:noinspection GoDeprecation
return Image{
&zip.FileHeader{
Name: filename,
CompressedSize64: uint64(cdata.Len()),
UncompressedSize64: uint64(len(uncompressedData)),
CRC32: crc32.Checksum(uncompressedData, 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),
},
cdata.Bytes(),
}, nil
}

View File

@ -50,24 +50,3 @@ func (e StorageImageWriter) Add(filename string, img image.Image, quality int) e
return nil return nil
} }
func (e StorageImageWriter) AddRaw(filename string, uncompressedData []byte) error {
zipImage, err := CompressRaw(filename, uncompressedData)
if err != nil {
return err
}
e.mut.Lock()
defer e.mut.Unlock()
fh, err := e.fz.CreateRaw(zipImage.Header)
if err != nil {
return err
}
_, err = fh.Write(zipImage.Data)
if err != nil {
return err
}
return nil
}

View File

@ -15,8 +15,8 @@ import (
"github.com/tcnksm/go-latest" "github.com/tcnksm/go-latest"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/converter" "github.com/celogeek/go-comic-converter/v3/internal/pkg/converter"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/epub"
"github.com/celogeek/go-comic-converter/v3/internal/pkg/utils" "github.com/celogeek/go-comic-converter/v3/internal/pkg/utils"
"github.com/celogeek/go-comic-converter/v3/pkg/epub"
) )
func main() { func main() {