From a816350c9706afaf0a1d2b079b7945801c520e09 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Mon, 1 May 2023 18:58:43 +0200 Subject: [PATCH] add png output images format --- internal/converter/converter.go | 6 +++++ .../converter/options/converter_options.go | 3 +++ internal/epub/epub.go | 2 +- internal/epub/image/epub_image.go | 3 ++- .../imageprocessor/epub_image_processor.go | 23 ++++++++++++++----- internal/epub/options/epub_options.go | 1 + .../epub/templates/epub_templates_content.go | 4 ++-- internal/epub/zip/epub_zip_image.go | 13 +++++++++-- internal/epub/zip/epub_zip_storage_image.go | 14 +++++------ main.go | 1 + 10 files changed, 51 insertions(+), 19 deletions(-) diff --git a/internal/converter/converter.go b/internal/converter/converter.go index 086caa3..4e1ba66 100644 --- a/internal/converter/converter.go +++ b/internal/converter/converter.go @@ -121,6 +121,7 @@ func (c *Converter) InitParse() { c.AddStringParam(&c.Options.ForegroundColor, "foreground-color", c.Options.ForegroundColor, "Foreground color in hexa format RGB. Black=000, White=FFF") c.AddStringParam(&c.Options.BackgroundColor, "background-color", c.Options.BackgroundColor, "Background color in hexa format RGB. Black=000, White=FFF, Light Gray=DDD, Dark Gray=777") c.AddBoolParam(&c.Options.NoResize, "noresize", c.Options.NoResize, "Do not reduce image size if exceed device size") + c.AddStringParam(&c.Options.Format, "format", c.Options.Format, "Format of output images: jpeg (lossy), png (lossless)") c.AddSection("Default config") c.AddBoolParam(&c.Options.Show, "show", false, "Show your default parameters") @@ -318,6 +319,11 @@ func (c *Converter) Validate() error { return errors.New("background color must have color format in hexa: [0-9A-F]{3}") } + // Format + if !(c.Options.Format == "jpeg" || c.Options.Format == "png") { + return errors.New("format should be jpeg or png") + } + return nil } diff --git a/internal/converter/options/converter_options.go b/internal/converter/options/converter_options.go index 035ee17..7816c91 100644 --- a/internal/converter/options/converter_options.go +++ b/internal/converter/options/converter_options.go @@ -43,6 +43,7 @@ type Options struct { ForegroundColor string `yaml:"foreground_color"` BackgroundColor string `yaml:"background_color"` NoResize bool `yaml:"noresize"` + Format string `yaml:"format"` // Default Config Show bool `yaml:"-"` @@ -86,6 +87,7 @@ func New() *Options { ForegroundColor: "000", BackgroundColor: "FFF", NoResize: false, + Format: "jpeg", profiles: profiles.New(), } } @@ -195,6 +197,7 @@ func (o *Options) ShowConfig() string { {"Foreground Color", fmt.Sprintf("#%s", o.ForegroundColor)}, {"Background Color", fmt.Sprintf("#%s", o.BackgroundColor)}, {"Resize", !o.NoResize}, + {"Format", o.Format}, } { b.WriteString(fmt.Sprintf("\n %-26s: %v", v.K, v.V)) } diff --git a/internal/epub/epub.go b/internal/epub/epub.go index d833a3d..380082a 100644 --- a/internal/epub/epub.go +++ b/internal/epub/epub.go @@ -282,7 +282,7 @@ func (e *ePub) Write() error { {"OEBPS/Text/title.xhtml", e.render(epubtemplates.Text, map[string]any{ "Title": title, "ViewPort": fmt.Sprintf("width=%d,height=%d", e.Image.View.Width, e.Image.View.Height), - "ImagePath": "Images/title.jpg", + "ImagePath": fmt.Sprintf("Images/title.%s", e.Image.Format), "ImageStyle": part.Cover.ImgStyle(e.Image.View.Width, e.Image.View.Height, titleAlign), })}, } diff --git a/internal/epub/image/epub_image.go b/internal/epub/image/epub_image.go index 8f74281..f6d5e73 100644 --- a/internal/epub/image/epub_image.go +++ b/internal/epub/image/epub_image.go @@ -21,6 +21,7 @@ type Image struct { Path string Name string Position string + Format string } // key name of the blank plage after the image @@ -60,7 +61,7 @@ func (i *Image) ImgKey() string { // image path func (i *Image) ImgPath() string { - return fmt.Sprintf("Images/%s.jpg", i.ImgKey()) + return fmt.Sprintf("Images/%s.%s", i.ImgKey(), i.Format) } // image path into the EPUB diff --git a/internal/epub/imageprocessor/epub_image_processor.go b/internal/epub/imageprocessor/epub_image_processor.go index ff1cafa..e308255 100644 --- a/internal/epub/imageprocessor/epub_image_processor.go +++ b/internal/epub/imageprocessor/epub_image_processor.go @@ -38,9 +38,10 @@ func (e *EPUBImageProcessor) Load() (images []*epubimage.Image, err error) { if e.Dry { for img := range imageInput { images = append(images, &epubimage.Image{ - Id: img.Id, - Path: img.Path, - Name: img.Name, + Id: img.Id, + Path: img.Path, + Name: img.Name, + Format: e.Image.Format, }) } @@ -59,13 +60,17 @@ func (e *EPUBImageProcessor) Load() (images []*epubimage.Image, err error) { }) wg := &sync.WaitGroup{} - imgStorage, err := epubzip.NewEPUBZipStorageImageWriter(e.ImgStorage()) + imgStorage, err := epubzip.NewEPUBZipStorageImageWriter(e.ImgStorage(), e.Image.Format) if err != nil { bar.Close() return nil, err } - for i := 0; i < e.WorkersRatio(50); i++ { + wr := 50 + if e.Image.Format == "png" { + wr = 100 + } + for i := 0; i < e.WorkersRatio(wr); i++ { wg.Add(1) go func() { defer wg.Done() @@ -90,6 +95,7 @@ func (e *EPUBImageProcessor) Load() (images []*epubimage.Image, err error) { DoublePage: part == 0 && src.Bounds().Dx() > src.Bounds().Dy(), Path: input.Path, Name: input.Name, + Format: e.Image.Format, } if err = imgStorage.Add(img.EPUBImgPath(), dst, e.Image.Quality); err != nil { @@ -253,5 +259,10 @@ func (e *EPUBImageProcessor) CoverTitleData(src image.Image, title string) (*epu dst := e.createImage(src, g.Bounds(src.Bounds())) g.Draw(dst, src) - return epubzip.CompressImage("OEBPS/Images/title.jpg", dst, e.Image.Quality) + return epubzip.CompressImage( + fmt.Sprintf("OEBPS/Images/title.%s", e.Image.Format), + e.Image.Format, + dst, + e.Image.Quality, + ) } diff --git a/internal/epub/options/epub_options.go b/internal/epub/options/epub_options.go index 9432b98..427852e 100644 --- a/internal/epub/options/epub_options.go +++ b/internal/epub/options/epub_options.go @@ -32,6 +32,7 @@ type Image struct { View *View GrayScale bool Resize bool + Format string } type Options struct { diff --git a/internal/epub/templates/epub_templates_content.go b/internal/epub/templates/epub_templates_content.go index 7271032..835b887 100644 --- a/internal/epub/templates/epub_templates_content.go +++ b/internal/epub/templates/epub_templates_content.go @@ -127,7 +127,7 @@ func getManifest(o *ContentOptions) []tag { var imageTags, pageTags, spaceTags []tag addTag := func(img *epubimage.Image, withSpace bool) { imageTags = append(imageTags, - tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": "image/jpeg"}, ""}, + tag{"item", tagAttrs{"id": img.ImgKey(), "href": img.ImgPath(), "media-type": fmt.Sprintf("image/%s", o.ImageOptions.Format)}, ""}, ) pageTags = append(pageTags, tag{"item", tagAttrs{"id": img.PageKey(), "href": img.PagePath(), "media-type": "application/xhtml+xml"}, ""}, @@ -144,7 +144,7 @@ func getManifest(o *ContentOptions) []tag { {"item", tagAttrs{"id": "css", "href": "Text/style.css", "media-type": "text/css"}, ""}, {"item", tagAttrs{"id": "space_title", "href": "Text/space_title.xhtml", "media-type": "application/xhtml+xml"}, ""}, {"item", tagAttrs{"id": "page_title", "href": "Text/title.xhtml", "media-type": "application/xhtml+xml"}, ""}, - {"item", tagAttrs{"id": "img_title", "href": "Images/title.jpg", "media-type": "image/jpeg"}, ""}, + {"item", tagAttrs{"id": "img_title", "href": fmt.Sprintf("Images/title.%s", o.ImageOptions.Format), "media-type": fmt.Sprintf("image/%s", o.ImageOptions.Format)}, ""}, } if o.ImageOptions.HasCover || o.Current > 1 { diff --git a/internal/epub/zip/epub_zip_image.go b/internal/epub/zip/epub_zip_image.go index 59070f6..1439687 100644 --- a/internal/epub/zip/epub_zip_image.go +++ b/internal/epub/zip/epub_zip_image.go @@ -4,9 +4,11 @@ import ( "archive/zip" "bytes" "compress/flate" + "fmt" "hash/crc32" "image" "image/jpeg" + "image/png" "time" ) @@ -16,13 +18,20 @@ type ZipImage struct { } // create gzip encoded jpeg -func CompressImage(filename string, img image.Image, quality int) (*ZipImage, error) { +func CompressImage(filename string, format string, img image.Image, quality int) (*ZipImage, error) { var ( data, cdata bytes.Buffer err error ) - err = jpeg.Encode(&data, img, &jpeg.Options{Quality: quality}) + switch format { + case "png": + err = png.Encode(&data, img) + case "jpeg": + err = jpeg.Encode(&data, img, &jpeg.Options{Quality: quality}) + default: + err = fmt.Errorf("unknown format %q", format) + } if err != nil { return nil, err } diff --git a/internal/epub/zip/epub_zip_storage_image.go b/internal/epub/zip/epub_zip_storage_image.go index 99ca4db..7073315 100644 --- a/internal/epub/zip/epub_zip_storage_image.go +++ b/internal/epub/zip/epub_zip_storage_image.go @@ -8,19 +8,19 @@ import ( ) type EPUBZipStorageImageWriter struct { - fh *os.File - fz *zip.Writer - - mut *sync.Mutex + fh *os.File + fz *zip.Writer + format string + mut *sync.Mutex } -func NewEPUBZipStorageImageWriter(filename string) (*EPUBZipStorageImageWriter, error) { +func NewEPUBZipStorageImageWriter(filename string, format string) (*EPUBZipStorageImageWriter, error) { fh, err := os.Create(filename) if err != nil { return nil, err } fz := zip.NewWriter(fh) - return &EPUBZipStorageImageWriter{fh, fz, &sync.Mutex{}}, nil + return &EPUBZipStorageImageWriter{fh, fz, format, &sync.Mutex{}}, nil } func (e *EPUBZipStorageImageWriter) Close() error { @@ -32,7 +32,7 @@ func (e *EPUBZipStorageImageWriter) Close() error { } func (e *EPUBZipStorageImageWriter) Add(filename string, img image.Image, quality int) error { - zipImage, err := CompressImage(filename, img, quality) + zipImage, err := CompressImage(filename, e.format, img, quality) if err != nil { return err } diff --git a/main.go b/main.go index 2f4c0a6..d354def 100644 --- a/main.go +++ b/main.go @@ -139,6 +139,7 @@ $ go install github.com/celogeek/go-comic-converter/v%d@%s }, }, Resize: !cmd.Options.NoResize, + Format: cmd.Options.Format, }, }).Write(); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err)