mirror of
https://github.com/celogeek/go-comic-converter.git
synced 2025-05-25 16:22:37 +02:00
Compare commits
8 Commits
ef27485273
...
418d51662c
Author | SHA1 | Date | |
---|---|---|---|
418d51662c | |||
88eea07747 | |||
51c04fecb0 | |||
d96e63bc50 | |||
b27a826cd2 | |||
4a43760535 | |||
bc19e5e4ce | |||
7f195a0b79 |
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/celogeek/go-comic-converter/v2/internal/converter/profiles"
|
"github.com/celogeek/go-comic-converter/v2/internal/converter/profiles"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@ -111,7 +112,7 @@ func (o *Options) LoadDefault() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *Options) ShowDefault() string {
|
func (o *Options) ShowDefault() string {
|
||||||
var profileDesc string
|
var profileDesc, viewDesc string
|
||||||
profile := o.GetProfile()
|
profile := o.GetProfile()
|
||||||
if profile != nil {
|
if profile != nil {
|
||||||
profileDesc = fmt.Sprintf(
|
profileDesc = fmt.Sprintf(
|
||||||
@ -121,6 +122,13 @@ func (o *Options) ShowDefault() string {
|
|||||||
profile.Width,
|
profile.Width,
|
||||||
profile.Height,
|
profile.Height,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
perfectWidth, perfectHeight := profile.PerfectDim()
|
||||||
|
viewDesc = fmt.Sprintf(
|
||||||
|
"%dx%d",
|
||||||
|
perfectWidth,
|
||||||
|
perfectHeight,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
limitmb := "nolimit"
|
limitmb := "nolimit"
|
||||||
if o.LimitMb > 0 {
|
if o.LimitMb > 0 {
|
||||||
@ -139,6 +147,8 @@ func (o *Options) ShowDefault() string {
|
|||||||
|
|
||||||
return fmt.Sprintf(`
|
return fmt.Sprintf(`
|
||||||
Profile : %s
|
Profile : %s
|
||||||
|
ViewRatio : 1:%s
|
||||||
|
View : %s
|
||||||
Quality : %d
|
Quality : %d
|
||||||
Crop : %v
|
Crop : %v
|
||||||
Brightness : %d
|
Brightness : %d
|
||||||
@ -152,6 +162,8 @@ func (o *Options) ShowDefault() string {
|
|||||||
StripFirstDirectoryFromToc: %v
|
StripFirstDirectoryFromToc: %v
|
||||||
SortPathMode : %s`,
|
SortPathMode : %s`,
|
||||||
profileDesc,
|
profileDesc,
|
||||||
|
strings.TrimRight(fmt.Sprintf("%f", profiles.PerfectRatio), "0"),
|
||||||
|
viewDesc,
|
||||||
o.Quality,
|
o.Quality,
|
||||||
o.Crop,
|
o.Crop,
|
||||||
o.Brightness,
|
o.Brightness,
|
@ -12,6 +12,19 @@ type Profile struct {
|
|||||||
Height int
|
Height int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const PerfectRatio = 1.5
|
||||||
|
|
||||||
|
func (p Profile) PerfectDim() (int, int) {
|
||||||
|
width, height := float64(p.Width), float64(p.Height)
|
||||||
|
perfectWidth, perfectHeight := height/PerfectRatio, width*PerfectRatio
|
||||||
|
if perfectWidth > width {
|
||||||
|
perfectWidth = width
|
||||||
|
} else {
|
||||||
|
perfectHeight = height
|
||||||
|
}
|
||||||
|
return int(perfectWidth), int(perfectHeight)
|
||||||
|
}
|
||||||
|
|
||||||
type Profiles []Profile
|
type Profiles []Profile
|
||||||
|
|
||||||
func New() Profiles {
|
func New() Profiles {
|
@ -97,14 +97,10 @@ func (e *ePub) writeImage(wz *epubZip, img *Image) error {
|
|||||||
err := wz.WriteFile(
|
err := wz.WriteFile(
|
||||||
fmt.Sprintf("OEBPS/%s", img.TextPath()),
|
fmt.Sprintf("OEBPS/%s", img.TextPath()),
|
||||||
e.render(textTmpl, map[string]any{
|
e.render(textTmpl, map[string]any{
|
||||||
"Title": fmt.Sprintf("Image %d Part %d", img.Id, img.Part),
|
"Title": fmt.Sprintf("Image %d Part %d", img.Id, img.Part),
|
||||||
"ViewPort": fmt.Sprintf("width=%d, height=%d", e.ViewWidth, e.ViewHeight),
|
"ViewPort": fmt.Sprintf("width=%d,height=%d", e.ViewWidth, e.ViewHeight),
|
||||||
"ImageStyle": fmt.Sprintf(
|
"ImagePath": img.ImgPath(),
|
||||||
"width:%dpx; height:%dpx;",
|
"ImageStyle": img.ImgStyle(e.ViewWidth, e.ViewHeight, e.Manga),
|
||||||
img.Width,
|
|
||||||
img.Height,
|
|
||||||
),
|
|
||||||
"ImagePath": img.ImgPath(),
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -120,7 +116,7 @@ func (e *ePub) writeBlank(wz *epubZip, img *Image) error {
|
|||||||
fmt.Sprintf("OEBPS/Text/%d_sp.xhtml", img.Id),
|
fmt.Sprintf("OEBPS/Text/%d_sp.xhtml", img.Id),
|
||||||
e.render(blankTmpl, map[string]any{
|
e.render(blankTmpl, map[string]any{
|
||||||
"Title": fmt.Sprintf("Blank Page %d", img.Id),
|
"Title": fmt.Sprintf("Blank Page %d", img.Id),
|
||||||
"ViewPort": fmt.Sprintf("width=%d, height=%d", e.ViewWidth, e.ViewHeight),
|
"ViewPort": fmt.Sprintf("width=%d,height=%d", e.ViewWidth, e.ViewHeight),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -24,7 +24,6 @@ func (e *ePub) getMeta(title string, part *epubPart, currentPart, totalPart int)
|
|||||||
{"meta", TagAttrs{"property": "rendition:layout"}, "pre-paginated"},
|
{"meta", TagAttrs{"property": "rendition:layout"}, "pre-paginated"},
|
||||||
{"meta", TagAttrs{"property": "rendition:spread"}, "auto"},
|
{"meta", TagAttrs{"property": "rendition:spread"}, "auto"},
|
||||||
{"meta", TagAttrs{"property": "rendition:orientation"}, "auto"},
|
{"meta", TagAttrs{"property": "rendition:orientation"}, "auto"},
|
||||||
{"meta", TagAttrs{"property": "ibooks:specified-fonts"}, "true"},
|
|
||||||
{"meta", TagAttrs{"property": "schema:accessMode"}, "visual"},
|
{"meta", TagAttrs{"property": "schema:accessMode"}, "visual"},
|
||||||
{"meta", TagAttrs{"property": "schema:accessModeSufficient"}, "visual"},
|
{"meta", TagAttrs{"property": "schema:accessModeSufficient"}, "visual"},
|
||||||
{"meta", TagAttrs{"property": "schema:accessibilityHazard"}, "noFlashingHazard"},
|
{"meta", TagAttrs{"property": "schema:accessibilityHazard"}, "noFlashingHazard"},
|
||||||
@ -151,7 +150,7 @@ func (e *ePub) getContent(title string, part *epubPart, currentPart, totalPart i
|
|||||||
pkg.CreateAttr("xmlns", "http://www.idpf.org/2007/opf")
|
pkg.CreateAttr("xmlns", "http://www.idpf.org/2007/opf")
|
||||||
pkg.CreateAttr("unique-identifier", "ean")
|
pkg.CreateAttr("unique-identifier", "ean")
|
||||||
pkg.CreateAttr("version", "3.0")
|
pkg.CreateAttr("version", "3.0")
|
||||||
pkg.CreateAttr("prefix", "rendition: http://www.idpf.org/vocab/rendition/# ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/")
|
pkg.CreateAttr("prefix", "rendition: http://www.idpf.org/vocab/rendition/#")
|
||||||
|
|
||||||
addToElement := func(elm *etree.Element, meth func(title string, part *epubPart, currentPart, totalPart int) []Tag) {
|
addToElement := func(elm *etree.Element, meth func(title string, part *epubPart, currentPart, totalPart int) []Tag) {
|
||||||
for _, p := range meth(title, part, currentPart, totalPart) {
|
for _, p := range meth(title, part, currentPart, totalPart) {
|
@ -21,7 +21,6 @@ func NewGift(options *ImageOptions) *gift.GIFT {
|
|||||||
g.Add(
|
g.Add(
|
||||||
filters.Resize(options.ViewWidth, options.ViewHeight, gift.LanczosResampling),
|
filters.Resize(options.ViewWidth, options.ViewHeight, gift.LanczosResampling),
|
||||||
filters.Pixel(),
|
filters.Pixel(),
|
||||||
filters.Position(options.ViewWidth, options.ViewHeight, filters.PositionCenter),
|
|
||||||
)
|
)
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
@ -37,7 +36,7 @@ func NewGiftSplitDoublePage(options *ImageOptions) []*gift.GIFT {
|
|||||||
filters.CropSplitDoublePage(!options.Manga),
|
filters.CropSplitDoublePage(!options.Manga),
|
||||||
)
|
)
|
||||||
|
|
||||||
for i, g := range gifts {
|
for _, g := range gifts {
|
||||||
if options.Contrast != 0 {
|
if options.Contrast != 0 {
|
||||||
g.Add(gift.Contrast(float32(options.Contrast)))
|
g.Add(gift.Contrast(float32(options.Contrast)))
|
||||||
}
|
}
|
||||||
@ -45,14 +44,8 @@ func NewGiftSplitDoublePage(options *ImageOptions) []*gift.GIFT {
|
|||||||
g.Add(gift.Brightness(float32(options.Brightness)))
|
g.Add(gift.Brightness(float32(options.Brightness)))
|
||||||
}
|
}
|
||||||
|
|
||||||
position := filters.PositionLeft
|
|
||||||
if (i == 1) == options.Manga {
|
|
||||||
position = filters.PositionRight
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Add(
|
g.Add(
|
||||||
filters.Resize(options.ViewWidth, options.ViewHeight, gift.LanczosResampling),
|
filters.Resize(options.ViewWidth, options.ViewHeight, gift.LanczosResampling),
|
||||||
filters.Position(options.ViewWidth, options.ViewHeight, position),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +53,36 @@ func (i *Image) ImgPath() string {
|
|||||||
return fmt.Sprintf("Images/%d_p%d.jpg", i.Id, i.Part)
|
return fmt.Sprintf("Images/%d_p%d.jpg", i.Id, i.Part)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) ImgStyle(viewWidth, viewHeight int, manga bool) string {
|
||||||
|
marginW, marginH := float64(viewWidth-i.Width)/2, float64(viewHeight-i.Height)/2
|
||||||
|
left, top := marginW*100/float64(viewWidth), marginH*100/float64(viewHeight)
|
||||||
|
var align string
|
||||||
|
switch i.Part {
|
||||||
|
case 0:
|
||||||
|
align = fmt.Sprintf("left:%.2f%%", left)
|
||||||
|
case 1:
|
||||||
|
if manga {
|
||||||
|
align = "left:0"
|
||||||
|
} else {
|
||||||
|
align = "right:0"
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if manga {
|
||||||
|
align = "right:0"
|
||||||
|
} else {
|
||||||
|
align = "left:0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"width:%dpx; height:%dpx; top:%.2f%%; %s;",
|
||||||
|
i.Width,
|
||||||
|
i.Height,
|
||||||
|
top,
|
||||||
|
align,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Image) SpacePath() string {
|
func (i *Image) SpacePath() string {
|
||||||
return fmt.Sprintf("Text/%d_sp.xhtml", i.Id)
|
return fmt.Sprintf("Text/%d_sp.xhtml", i.Id)
|
||||||
}
|
}
|
21
internal/epub/epub_templates.go
Normal file
21
internal/epub/epub_templates.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package epub
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed "templates/epub_templates_container.xml.tmpl"
|
||||||
|
var containerTmpl string
|
||||||
|
|
||||||
|
//go:embed "templates/epub_templates_applebooks.xml.tmpl"
|
||||||
|
var appleBooksTmpl string
|
||||||
|
|
||||||
|
//go:embed "templates/epub_templates_style.css.tmpl"
|
||||||
|
var styleTmpl string
|
||||||
|
|
||||||
|
//go:embed "templates/epub_templates_title.xhtml.tmpl"
|
||||||
|
var titleTmpl string
|
||||||
|
|
||||||
|
//go:embed "templates/epub_templates_text.xhtml.tmpl"
|
||||||
|
var textTmpl string
|
||||||
|
|
||||||
|
//go:embed "templates/epub_templates_blank.xhtml.tmpl"
|
||||||
|
var blankTmpl string
|
@ -44,9 +44,14 @@ func (e *ePub) getToc(title string, images []*Image) string {
|
|||||||
if len(ol.ChildElements()) == 1 && e.StripFirstDirectoryFromToc {
|
if len(ol.ChildElements()) == 1 && e.StripFirstDirectoryFromToc {
|
||||||
ol = ol.ChildElements()[0]
|
ol = ol.ChildElements()[0]
|
||||||
}
|
}
|
||||||
if len(ol.ChildElements()) > 0 {
|
|
||||||
nav.AddChild(ol)
|
beginning := etree.NewElement("li")
|
||||||
}
|
beginningLink := beginning.CreateElement("a")
|
||||||
|
beginningLink.CreateAttr("href", images[0].TextPath())
|
||||||
|
beginningLink.CreateText("Start of the book")
|
||||||
|
ol.InsertChildAt(0, beginning)
|
||||||
|
|
||||||
|
nav.AddChild(ol)
|
||||||
|
|
||||||
doc.Indent(2)
|
doc.Indent(2)
|
||||||
r, _ := doc.WriteToString()
|
r, _ := doc.WriteToString()
|
@ -1,55 +0,0 @@
|
|||||||
package filters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"image"
|
|
||||||
"image/draw"
|
|
||||||
|
|
||||||
"github.com/disintegration/gift"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PositionCenter = iota
|
|
||||||
PositionLeft
|
|
||||||
PositionRight
|
|
||||||
)
|
|
||||||
|
|
||||||
func Position(viewWidth, viewHeight int, align int) gift.Filter {
|
|
||||||
return &positionFilter{
|
|
||||||
viewWidth, viewHeight, align,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type positionFilter struct {
|
|
||||||
viewWidth, viewHeight, align int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *positionFilter) Bounds(srcBounds image.Rectangle) image.Rectangle {
|
|
||||||
return image.Rect(0, 0, p.viewWidth, p.viewHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *positionFilter) Draw(dst draw.Image, src image.Image, options *gift.Options) {
|
|
||||||
draw.Draw(dst, dst.Bounds(), image.White, dst.Bounds().Min, draw.Over)
|
|
||||||
|
|
||||||
srcBounds := src.Bounds()
|
|
||||||
left, top := (p.viewWidth-srcBounds.Dx())/2, (p.viewHeight-srcBounds.Dy())/2
|
|
||||||
if p.align == PositionLeft {
|
|
||||||
left = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.align == PositionRight {
|
|
||||||
left = p.viewWidth - srcBounds.Dx()
|
|
||||||
}
|
|
||||||
|
|
||||||
draw.Draw(
|
|
||||||
dst,
|
|
||||||
image.Rect(
|
|
||||||
left,
|
|
||||||
top,
|
|
||||||
p.viewWidth,
|
|
||||||
p.viewHeight,
|
|
||||||
),
|
|
||||||
src,
|
|
||||||
srcBounds.Min,
|
|
||||||
draw.Over,
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
package epub
|
|
||||||
|
|
||||||
import _ "embed"
|
|
||||||
|
|
||||||
//go:embed "templates/container.xml.tmpl"
|
|
||||||
var containerTmpl string
|
|
||||||
|
|
||||||
//go:embed "templates/applebooks.xml.tmpl"
|
|
||||||
var appleBooksTmpl string
|
|
||||||
|
|
||||||
//go:embed "templates/style.css.tmpl"
|
|
||||||
var styleTmpl string
|
|
||||||
|
|
||||||
//go:embed "templates/title.xhtml.tmpl"
|
|
||||||
var titleTmpl string
|
|
||||||
|
|
||||||
//go:embed "templates/text.xhtml.tmpl"
|
|
||||||
var textTmpl string
|
|
||||||
|
|
||||||
//go:embed "templates/blank.xhtml.tmpl"
|
|
||||||
var blankTmpl string
|
|
@ -2,6 +2,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
<title>{{ .Title }}</title>
|
<title>{{ .Title }}</title>
|
||||||
<link href="style.css" type="text/css" rel="stylesheet"/>
|
<link href="style.css" type="text/css" rel="stylesheet"/>
|
||||||
<meta name="viewport" content="{{ .ViewPort }}"/>
|
<meta name="viewport" content="{{ .ViewPort }}"/>
|
@ -1,13 +1,18 @@
|
|||||||
body {
|
body {
|
||||||
color: #000;
|
color: #000;
|
||||||
background: #FFF;
|
background: #FFF;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: {{ .PageWidth }}px;
|
width: {{ .PageWidth }}px;
|
||||||
height: {{ .PageHeight }}px;
|
height: {{ .PageHeight }}px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
div {
|
img {
|
||||||
|
position: absolute;
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
|
z-index:0;
|
||||||
}
|
}
|
@ -2,13 +2,14 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
<title>{{ .Title }}</title>
|
<title>{{ .Title }}</title>
|
||||||
<link href="style.css" type="text/css" rel="stylesheet"/>
|
<link href="style.css" type="text/css" rel="stylesheet"/>
|
||||||
<meta name="viewport" content="{{ .ViewPort }}"/>
|
<meta name="viewport" content="{{ .ViewPort }}"/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<img style="{{ .ImageStyle }}" src="../{{ .ImagePath }}"/>
|
<img src="../{{ .ImagePath }}" alt="{{ .Title }}" style="{{ .ImageStyle }}"/>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -2,6 +2,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
<title>Part {{ .Part }}</title>
|
<title>Part {{ .Part }}</title>
|
||||||
<link href="style.css" type="text/css" rel="stylesheet"/>
|
<link href="style.css" type="text/css" rel="stylesheet"/>
|
||||||
<meta name="viewport" content="width={{ .Info.ViewWidth }}, height={{ .Info.ViewHeight }}"/>
|
<meta name="viewport" content="width={{ .Info.ViewWidth }}, height={{ .Info.ViewHeight }}"/>
|
6
main.go
6
main.go
@ -91,6 +91,8 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s
|
|||||||
fmt.Fprintln(os.Stderr, cmd.Options)
|
fmt.Fprintln(os.Stderr, cmd.Options)
|
||||||
|
|
||||||
profile := cmd.Options.GetProfile()
|
profile := cmd.Options.GetProfile()
|
||||||
|
perfectWidth, perfectHeight := profile.PerfectDim()
|
||||||
|
|
||||||
if err := epub.NewEpub(&epub.EpubOptions{
|
if err := epub.NewEpub(&epub.EpubOptions{
|
||||||
Input: cmd.Options.Input,
|
Input: cmd.Options.Input,
|
||||||
Output: cmd.Options.Output,
|
Output: cmd.Options.Output,
|
||||||
@ -102,8 +104,8 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s
|
|||||||
DryVerbose: cmd.Options.DryVerbose,
|
DryVerbose: cmd.Options.DryVerbose,
|
||||||
SortPathMode: cmd.Options.SortPathMode,
|
SortPathMode: cmd.Options.SortPathMode,
|
||||||
ImageOptions: &epub.ImageOptions{
|
ImageOptions: &epub.ImageOptions{
|
||||||
ViewWidth: profile.Width,
|
ViewWidth: perfectWidth,
|
||||||
ViewHeight: profile.Height,
|
ViewHeight: perfectHeight,
|
||||||
Quality: cmd.Options.Quality,
|
Quality: cmd.Options.Quality,
|
||||||
Crop: cmd.Options.Crop,
|
Crop: cmd.Options.Crop,
|
||||||
Brightness: cmd.Options.Brightness,
|
Brightness: cmd.Options.Brightness,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user