mirror of
https://github.com/celogeek/go-comic-converter.git
synced 2025-05-25 16:22:37 +02:00
Compare commits
15 Commits
16a12603a0
...
c2ea8ff5b9
Author | SHA1 | Date | |
---|---|---|---|
c2ea8ff5b9 | |||
b10e9b1808 | |||
14be5a4ea3 | |||
c0bc8ea01a | |||
dfb9a6fff3 | |||
506cd1ad6c | |||
277dff8161 | |||
af261d3f75 | |||
ad614d09b4 | |||
86fbb8cefe | |||
2d21ced2fe | |||
05dd8acc99 | |||
87a127d04a | |||
a5ef2a2927 | |||
673a7df699 |
64
README.md
64
README.md
@ -89,6 +89,8 @@ 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
|
||||||
@ -443,34 +445,34 @@ Output:
|
|||||||
Config:
|
Config:
|
||||||
-profile string (default "SR")
|
-profile string (default "SR")
|
||||||
Profile to use:
|
Profile to use:
|
||||||
- HR ( 2400x3840 ) - High Resolution
|
- KoAO - 1404 x 1872 - Kobo Aura ONE
|
||||||
- SR ( 1200x1920 ) - Standard Resolution
|
- KoF - 1440 x 1920 - Kobo Forma
|
||||||
- K1 ( 600x670 ) - Kindle 1
|
- KoE - 1404 x 1872 - Kobo Elipsa
|
||||||
- K11 ( 1072x1448 ) - Kindle 11
|
- KV - 1072 x 1448 - Kindle Paperwhite 3/4/Voyage/Oasis
|
||||||
- K2 ( 600x670 ) - Kindle 2
|
- KoG - 768 x 1024 - Kobo Glo
|
||||||
- K34 ( 600x800 ) - Kindle Keyboard/Touch
|
- KoA - 758 x 1024 - Kobo Aura
|
||||||
- K578 ( 600x800 ) - Kindle
|
- RM1 - 1404 x 1872 - reMarkable 1
|
||||||
- KDX ( 824x1000 ) - Kindle DX/DXG
|
- RM2 - 1404 x 1872 - reMarkable 2
|
||||||
- KPW ( 758x1024 ) - Kindle Paperwhite 1/2
|
- K1 - 600 x 670 - Kindle 1
|
||||||
- KV ( 1072x1448 ) - Kindle Paperwhite 3/4/Voyage/Oasis
|
- K11 - 1072 x 1448 - Kindle 11
|
||||||
- KPW5 ( 1236x1648 ) - Kindle Paperwhite 5/Signature Edition
|
- K2 - 600 x 670 - Kindle 2
|
||||||
- KO ( 1264x1680 ) - Kindle Oasis 2/3
|
- K34 - 600 x 800 - Kindle Keyboard/Touch
|
||||||
- KS ( 1860x2480 ) - Kindle Scribe
|
- KPW5 - 1236 x 1648 - Kindle Paperwhite 5/Signature Edition
|
||||||
- KoMT ( 600x800 ) - Kobo Mini/Touch
|
- KoAH2O - 1080 x 1430 - Kobo Aura H2O
|
||||||
- KoG ( 768x1024 ) - Kobo Glo
|
- KoN - 758 x 1024 - Kobo Nia
|
||||||
- KoGHD ( 1072x1448 ) - Kobo Glo HD
|
- KoL - 1264 x 1680 - Kobo Libra H2O/Kobo Libra 2
|
||||||
- KoA ( 758x1024 ) - Kobo Aura
|
- HR - 2400 x 3840 - High Resolution
|
||||||
- KoAHD ( 1080x1440 ) - Kobo Aura HD
|
- KO - 1264 x 1680 - Kindle Oasis 2/3
|
||||||
- KoAH2O ( 1080x1430 ) - Kobo Aura H2O
|
- KS - 1860 x 2480 - Kindle Scribe
|
||||||
- KoAO ( 1404x1872 ) - Kobo Aura ONE
|
- KoMT - 600 x 800 - Kobo Mini/Touch
|
||||||
- KoN ( 758x1024 ) - Kobo Nia
|
- KoAHD - 1080 x 1440 - Kobo Aura HD
|
||||||
- KoC ( 1072x1448 ) - Kobo Clara HD/Kobo Clara 2E
|
- KoC - 1072 x 1448 - Kobo Clara HD/Kobo Clara 2E
|
||||||
- KoL ( 1264x1680 ) - Kobo Libra H2O/Kobo Libra 2
|
- KoS - 1440 x 1920 - Kobo Sage
|
||||||
- KoF ( 1440x1920 ) - Kobo Forma
|
- SR - 1200 x 1920 - Standard Resolution
|
||||||
- KoS ( 1440x1920 ) - Kobo Sage
|
- K578 - 600 x 800 - Kindle
|
||||||
- KoE ( 1404x1872 ) - Kobo Elipsa
|
- KDX - 824 x 1000 - Kindle DX/DXG
|
||||||
- RM1 ( 1404x1872 ) - reMarkable 1
|
- KPW - 758 x 1024 - Kindle Paperwhite 1/2
|
||||||
- RM2 ( 1404x1872 ) - reMarkable 2
|
- KoGHD - 1072 x 1448 - Kobo Glo HD
|
||||||
-quality int (default 85)
|
-quality int (default 85)
|
||||||
Quality of the image
|
Quality of the image
|
||||||
-grayscale (default true)
|
-grayscale (default true)
|
||||||
@ -527,10 +529,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
|
||||||
-noresize
|
-resize (default true)
|
||||||
Do not reduce image size if exceed device size
|
Reduce image size if exceed device size
|
||||||
-format string (default "jpeg")
|
-format string (default "jpeg")
|
||||||
Format of output images: jpeg (lossy), png (lossless)
|
Format of output images: jpeg (lossy), png (lossless), copy (no processing)
|
||||||
-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
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -131,7 +132,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)")
|
c.AddStringParam(&c.Options.Image.Format, "format", c.Options.Image.Format, "Format of output images: jpeg (lossy), png (lossless), copy (no processing)")
|
||||||
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")
|
||||||
@ -379,8 +380,8 @@ func (c *Converter) Validate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Format
|
// Format
|
||||||
if !(c.Options.Image.Format == "jpeg" || c.Options.Image.Format == "png") {
|
if !slices.Contains([]string{"jpeg", "png", "copy"}, c.Options.Image.Format) {
|
||||||
return errors.New("format should be jpeg or png")
|
return errors.New("format should be jpeg, png or copy")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aspect Ratio
|
// Aspect Ratio
|
||||||
|
@ -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, true},
|
{"Grayscale", o.Image.GrayScale, o.Image.Format != "copy"},
|
||||||
{"Grayscale mode", grayscaleMode, o.Image.GrayScale},
|
{"Grayscale mode", grayscaleMode, o.Image.Format != "copy" && o.Image.GrayScale},
|
||||||
{"Crop", o.Image.Crop.Enabled, true},
|
{"Crop", o.Image.Crop.Enabled, o.Image.Format != "copy"},
|
||||||
{"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.Crop.Enabled},
|
o.Image.Format != "copy" && o.Image.Crop.Enabled},
|
||||||
{"Brightness", o.Image.Brightness, o.Image.Brightness != 0},
|
{"Brightness", o.Image.Brightness, o.Image.Format != "copy" && o.Image.Brightness != 0},
|
||||||
{"Contrast", o.Image.Contrast, o.Image.Contrast != 0},
|
{"Contrast", o.Image.Contrast, o.Image.Format != "copy" && o.Image.Contrast != 0},
|
||||||
{"Auto contrast", o.Image.AutoContrast, true},
|
{"Auto contrast", o.Image.AutoContrast, o.Image.Format != "copy"},
|
||||||
{"Auto rotate", o.Image.AutoRotate, true},
|
{"Auto rotate", o.Image.AutoRotate, o.Image.Format != "copy"},
|
||||||
{"Auto split double page", o.Image.AutoSplitDoublePage, o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility},
|
{"Auto split double page", o.Image.AutoSplitDoublePage, o.Image.Format != "copy" && (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility)},
|
||||||
{"Keep double page if split", o.Image.KeepDoublePageIfSplit, (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility) && o.Image.AutoSplitDoublePage},
|
{"Keep double page if split", o.Image.KeepDoublePageIfSplit, 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},
|
{"Keep split double page aspect", o.Image.KeepSplitDoublePageAspect, o.Image.Format != "copy" && (o.Image.View.PortraitOnly || !o.Image.AppleBookCompatibility) && o.Image.AutoSplitDoublePage},
|
||||||
{"No blank image", o.Image.NoBlankImage, true},
|
{"No blank image", o.Image.NoBlankImage, o.Image.Format != "copy"},
|
||||||
{"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, true},
|
{"Resize", o.Image.Resize, o.Image.Format != "copy"},
|
||||||
{"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},
|
||||||
|
@ -73,6 +73,11 @@ 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.
|
||||||
|
432
internal/pkg/epubimagepassthrough/passthrough.go
Normal file
432
internal/pkg/epubimagepassthrough/passthrough.go
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
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
|
||||||
|
}
|
@ -43,7 +43,7 @@ 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":
|
||||||
{
|
{
|
||||||
@ -53,8 +53,8 @@ func (e EPUBImageProcessor) isSupportedImage(path string) bool {
|
|||||||
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")
|
||||||
|
@ -17,16 +17,21 @@ import (
|
|||||||
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
|
"github.com/celogeek/go-comic-converter/v3/pkg/epuboptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EPUBImageProcessor struct {
|
type EPUBImageProcessor interface {
|
||||||
|
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 {
|
||||||
@ -136,7 +141,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)
|
||||||
}
|
}
|
||||||
@ -169,7 +174,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()
|
||||||
@ -286,7 +291,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},
|
||||||
@ -308,20 +313,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+"."+e.Image.Format,
|
"OEBPS/Images/"+o.Name+".jpeg",
|
||||||
e.Image.Format,
|
"jpeg",
|
||||||
dst,
|
dst,
|
||||||
e.Image.Quality,
|
e.Image.Quality,
|
||||||
)
|
)
|
||||||
|
@ -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": o.ImageOptions.MediaType()}, ""},
|
tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": img.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." + o.ImageOptions.Format, "media-type": o.ImageOptions.MediaType()}, ""},
|
{"item", tagAttrs{"id": "img_cover", "href": "Images/cover.jpeg", "media-type": "image/jpeg"}, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
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." + o.ImageOptions.Format, "media-type": o.ImageOptions.MediaType()}, ""},
|
tag{"item", tagAttrs{"id": "img_title", "href": "Images/title.jpeg", "media-type": "image/jpeg"}, ""},
|
||||||
)
|
)
|
||||||
|
|
||||||
if !o.ImageOptions.View.PortraitOnly {
|
if !o.ImageOptions.View.PortraitOnly {
|
||||||
|
@ -66,3 +66,39 @@ 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
|
||||||
|
}
|
||||||
|
@ -50,3 +50,24 @@ 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
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ 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/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"
|
||||||
@ -52,13 +53,20 @@ 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
|
||||||
|
if options.Image.Format == "copy" {
|
||||||
|
imageProcessor = epubimagepassthrough.New(options)
|
||||||
|
} else {
|
||||||
|
imageProcessor = epubimageprocessor.New(options)
|
||||||
|
}
|
||||||
|
|
||||||
return epub{
|
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: epubimageprocessor.New(options),
|
imageProcessor: imageProcessor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +123,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." + e.Image.Format,
|
"ImagePath": "Images/cover.jpeg",
|
||||||
"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 {
|
||||||
@ -172,7 +180,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." + e.Image.Format,
|
"ImagePath": "Images/title.jpeg",
|
||||||
"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 {
|
||||||
|
@ -20,7 +20,3 @@ 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
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user