mirror of
https://github.com/gen2brain/cbconvert
synced 2026-06-30 09:11:54 +02:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dfea3dc6a3 | |||
| 16d01e292e | |||
| 6fe10f5bdb | |||
| 547a3937a8 | |||
| d47c1c1464 | |||
| 5befff07ae | |||
| d4abc99239 | |||
| f1804ce13c | |||
| a74034d669 | |||
| 64fb152c36 | |||
| 7082e3db90 | |||
| 943cd9bb95 | |||
| ec2c321ff3 | |||
| a0ed277971 | |||
| b5b4457088 | |||
| c24d7b7e18 |
@@ -18,10 +18,11 @@ It can convert comics to different formats to fit your various devices.
|
||||
|
||||
### Download
|
||||
|
||||
* [Windows x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-windows-x86_64.zip)
|
||||
* [Linux x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-linux-x86_64.tar.gz)
|
||||
* [macOS x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-darwin-x86_64.zip)
|
||||
* [macOS aarch64](https://github.com/gen2brain/cbconvert/releases/download/0.8.0/cbconvert-0.8.0-darwin-aarch64.zip)
|
||||
* [Windows x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.3/cbconvert-0.8.3-windows-x86_64.zip)
|
||||
* [Linux x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.3/cbconvert-0.8.3-linux-x86_64.tar.gz)
|
||||
* [Linux aarch64](https://github.com/gen2brain/cbconvert/releases/download/0.8.3/cbconvert-0.8.3-linux-aarch64.tar.gz)
|
||||
* [macOS x86_64](https://github.com/gen2brain/cbconvert/releases/download/0.8.3/cbconvert-0.8.3-darwin-x86_64.zip)
|
||||
* [macOS aarch64](https://github.com/gen2brain/cbconvert/releases/download/0.8.3/cbconvert-0.8.3-darwin-aarch64.zip)
|
||||
|
||||
### Using cbconvert in file managers to generate FreeDesktop thumbnails
|
||||
|
||||
@@ -112,8 +113,10 @@ This is what it looks like in the PCManFM file manager:
|
||||
Image height (default "0")
|
||||
--fit
|
||||
Best fit for required width and height (default "false")
|
||||
--format
|
||||
Image format, valid values are jpeg, png, tiff, bmp, webp, avif (default "jpeg")
|
||||
--quality
|
||||
JPEG image quality (default "75")
|
||||
Image quality (default "75")
|
||||
--filter
|
||||
0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
|
||||
--outdir
|
||||
@@ -146,6 +149,12 @@ This is what it looks like in the PCManFM file manager:
|
||||
Process subdirectories recursively (default "false")
|
||||
--quiet
|
||||
Hide console output (default "false")
|
||||
|
||||
meta
|
||||
CBZ metadata
|
||||
|
||||
--cover
|
||||
Print cover name (default "false")
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
+206
-151
@@ -6,12 +6,6 @@ import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
_ "image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"mime"
|
||||
"os"
|
||||
@@ -22,6 +16,13 @@ import (
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
_ "image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
|
||||
"github.com/chai2010/webp"
|
||||
_ "github.com/hotei/bmp"
|
||||
"github.com/strukturag/libheif/go/heif"
|
||||
@@ -93,6 +94,8 @@ type Options struct {
|
||||
Cover bool
|
||||
// Extract cover thumbnail (freedesktop spec.)
|
||||
Thumbnail bool
|
||||
// CBZ metadata
|
||||
Meta bool
|
||||
// Output file
|
||||
Outfile string
|
||||
// Output directory
|
||||
@@ -166,12 +169,11 @@ func (c *Convertor) convertImage(ctx context.Context, img image.Image, index int
|
||||
c.OnProgress()
|
||||
}
|
||||
|
||||
var ext = c.Opts.Format
|
||||
var fileName string
|
||||
if pathName != "" {
|
||||
fileName = filepath.Join(c.Workdir, fmt.Sprintf("%s.%s", c.baseNoExt(pathName), ext))
|
||||
fileName = filepath.Join(c.Workdir, fmt.Sprintf("%s.%s", c.baseNoExt(pathName), c.Opts.Format))
|
||||
} else {
|
||||
fileName = filepath.Join(c.Workdir, fmt.Sprintf("%03d.%s", index, ext))
|
||||
fileName = filepath.Join(c.Workdir, fmt.Sprintf("%03d.%s", index, c.Opts.Format))
|
||||
}
|
||||
|
||||
img = c.transformImage(img)
|
||||
@@ -186,14 +188,12 @@ func (c *Convertor) convertImage(ctx context.Context, img image.Image, index int
|
||||
|
||||
switch c.Opts.Format {
|
||||
case "jpeg", "png", "tiff", "webp", "avif":
|
||||
err = c.encodeImage(img, fileName)
|
||||
if err != nil {
|
||||
if err := c.encodeImage(img, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
case "bmp":
|
||||
// convert image to 4-Bit BMP (16 colors)
|
||||
err = c.encodeIM(img, fileName)
|
||||
if err != nil {
|
||||
if err := c.encodeIM(img, fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -246,8 +246,6 @@ func (c *Convertor) transformImage(img image.Image) image.Image {
|
||||
|
||||
// levelImage applies a Photoshop-like levels operation on an image.
|
||||
func (c *Convertor) levelImage(img image.Image) (image.Image, error) {
|
||||
imagick.Initialize()
|
||||
|
||||
mw := imagick.NewMagickWand()
|
||||
defer mw.Destroy()
|
||||
|
||||
@@ -265,13 +263,11 @@ func (c *Convertor) levelImage(img image.Image) (image.Image, error) {
|
||||
outmin := (quantumRange * c.Opts.LevelsOutMin) / 255
|
||||
outmax := (quantumRange * c.Opts.LevelsOutMax) / 255
|
||||
|
||||
err = mw.LevelImage(inmin, c.Opts.LevelsGamma, inmax)
|
||||
if err != nil {
|
||||
if err := mw.LevelImage(inmin, c.Opts.LevelsGamma, inmax); err != nil {
|
||||
return img, fmt.Errorf("levelImage: %w", err)
|
||||
}
|
||||
|
||||
err = mw.LevelImage(-outmin, 1.0, quantumRange+(quantumRange-outmax))
|
||||
if err != nil {
|
||||
if err := mw.LevelImage(-outmin, 1.0, quantumRange+(quantumRange-outmax)); err != nil {
|
||||
return img, fmt.Errorf("levelImage: %w", err)
|
||||
}
|
||||
|
||||
@@ -291,7 +287,12 @@ func (c *Convertor) levelImage(img image.Image) (image.Image, error) {
|
||||
|
||||
// convertDocument converts PDF/EPUB document to CBZ.
|
||||
func (c *Convertor) convertDocument(fileName string) error {
|
||||
c.Workdir, _ = os.MkdirTemp(os.TempDir(), "cbc")
|
||||
var err error
|
||||
|
||||
c.Workdir, err = os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDocument: %w", err)
|
||||
}
|
||||
|
||||
doc, err := fitz.New(fileName)
|
||||
if err != nil {
|
||||
@@ -328,7 +329,12 @@ func (c *Convertor) convertDocument(fileName string) error {
|
||||
|
||||
// convertArchive converts archive to CBZ.
|
||||
func (c *Convertor) convertArchive(fileName string) error {
|
||||
c.Workdir, _ = os.MkdirTemp(os.TempDir(), "cbc")
|
||||
var err error
|
||||
|
||||
c.Workdir, err = os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
contents, err := c.listArchive(fileName)
|
||||
if err != nil {
|
||||
@@ -374,8 +380,7 @@ func (c *Convertor) convertArchive(fileName string) error {
|
||||
|
||||
if c.isImage(pathName) {
|
||||
if c.Opts.NoConvert {
|
||||
err = c.copyFile(bytes.NewReader(data), filepath.Join(c.Workdir, filepath.Base(pathName)))
|
||||
if err != nil {
|
||||
if err = c.copyFile(bytes.NewReader(data), filepath.Join(c.Workdir, filepath.Base(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
@@ -393,8 +398,7 @@ func (c *Convertor) convertArchive(fileName string) error {
|
||||
|
||||
if cover == pathName && c.Opts.NoCover {
|
||||
img = c.transformImage(img)
|
||||
err = c.encodeImage(img, filepath.Join(c.Workdir, filepath.Base(pathName)))
|
||||
if err != nil {
|
||||
if err = c.encodeImage(img, filepath.Join(c.Workdir, filepath.Base(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
@@ -403,8 +407,7 @@ func (c *Convertor) convertArchive(fileName string) error {
|
||||
|
||||
if c.Opts.NoRGB && !c.isGrayScale(img) {
|
||||
img = c.transformImage(img)
|
||||
err = c.encodeImage(img, filepath.Join(c.Workdir, filepath.Base(pathName)))
|
||||
if err != nil {
|
||||
if err = c.encodeImage(img, filepath.Join(c.Workdir, filepath.Base(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
@@ -418,8 +421,7 @@ func (c *Convertor) convertArchive(fileName string) error {
|
||||
}
|
||||
} else {
|
||||
if !c.Opts.NoNonImage {
|
||||
err = c.copyFile(bytes.NewReader(data), filepath.Join(c.Workdir, filepath.Base(pathName)))
|
||||
if err != nil {
|
||||
if err = c.copyFile(bytes.NewReader(data), filepath.Join(c.Workdir, filepath.Base(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -431,7 +433,12 @@ func (c *Convertor) convertArchive(fileName string) error {
|
||||
|
||||
// convertDirectory converts directory to CBZ.
|
||||
func (c *Convertor) convertDirectory(dirPath string) error {
|
||||
c.Workdir, _ = os.MkdirTemp(os.TempDir(), "cbc")
|
||||
var err error
|
||||
|
||||
c.Workdir, err = os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
contents, err := c.imagesFromPath(dirPath)
|
||||
if err != nil {
|
||||
@@ -456,21 +463,22 @@ func (c *Convertor) convertDirectory(dirPath string) error {
|
||||
}
|
||||
|
||||
if c.isNonImage(img) && !c.Opts.NoNonImage {
|
||||
err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img)))
|
||||
if err != nil {
|
||||
if err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img))); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
continue
|
||||
} else if c.isImage(img) {
|
||||
if c.Opts.NoConvert {
|
||||
err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img)))
|
||||
if err != nil {
|
||||
if err = c.copyFile(file, filepath.Join(c.Workdir, filepath.Base(img))); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
@@ -488,21 +496,18 @@ func (c *Convertor) convertDirectory(dirPath string) error {
|
||||
|
||||
if c.Opts.NoRGB && !c.isGrayScale(i) {
|
||||
i = c.transformImage(i)
|
||||
err = c.encodeImage(i, filepath.Join(c.Workdir, filepath.Base(img)))
|
||||
if err != nil {
|
||||
if err = c.encodeImage(i, filepath.Join(c.Workdir, filepath.Base(img))); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
@@ -565,19 +570,30 @@ func (c *Convertor) saveArchive(fileName string) error {
|
||||
}
|
||||
}
|
||||
|
||||
err = z.Close()
|
||||
if err != nil {
|
||||
if err = z.Close(); err != nil {
|
||||
return fmt.Errorf("saveArchive: %w", err)
|
||||
}
|
||||
|
||||
err = zipfile.Close()
|
||||
if err != nil {
|
||||
if err = zipfile.Close(); err != nil {
|
||||
return fmt.Errorf("saveArchive: %w", err)
|
||||
}
|
||||
|
||||
return os.RemoveAll(c.Workdir)
|
||||
}
|
||||
|
||||
// listArchive lists contents of archive.
|
||||
func (c *Convertor) listArchive(fileName string) ([]string, error) {
|
||||
var contents []string
|
||||
|
||||
archive, err := unarr.NewArchive(fileName)
|
||||
if err != nil {
|
||||
return contents, err
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
return archive.List()
|
||||
}
|
||||
|
||||
// decodeImage decodes image from reader.
|
||||
func (c *Convertor) decodeImage(reader io.Reader, fileName string) (img image.Image, err error) {
|
||||
img, _, err = image.Decode(reader)
|
||||
@@ -590,8 +606,6 @@ func (c *Convertor) decodeImage(reader io.Reader, fileName string) (img image.Im
|
||||
|
||||
// decodeIM decodes image from reader (ImageMagick).
|
||||
func (c *Convertor) decodeIM(reader io.Reader, fileName string) (img image.Image, err error) {
|
||||
imagick.Initialize()
|
||||
|
||||
mw := imagick.NewMagickWand()
|
||||
defer mw.Destroy()
|
||||
|
||||
@@ -603,13 +617,11 @@ func (c *Convertor) decodeIM(reader io.Reader, fileName string) (img image.Image
|
||||
return img, fmt.Errorf("decodeIM: %w", err)
|
||||
}
|
||||
|
||||
err = mw.SetFilename(fileName)
|
||||
if err != nil {
|
||||
if err = mw.SetFilename(fileName); err != nil {
|
||||
return img, fmt.Errorf("decodeIM: %w", err)
|
||||
}
|
||||
|
||||
err = mw.ReadImageBlob(data)
|
||||
if err != nil {
|
||||
if err = mw.ReadImageBlob(data); err != nil {
|
||||
return img, fmt.Errorf("decodeIM: %w", err)
|
||||
}
|
||||
|
||||
@@ -672,67 +684,89 @@ func (c *Convertor) encodeImage(img image.Image, fileName string) error {
|
||||
|
||||
// encodeIM encodes image to file (ImageMagick).
|
||||
func (c *Convertor) encodeIM(i image.Image, fileName string) error {
|
||||
imagick.Initialize()
|
||||
|
||||
mw := imagick.NewMagickWand()
|
||||
defer mw.Destroy()
|
||||
|
||||
rgba := imageToRGBA(i)
|
||||
err := mw.ConstituteImage(uint(i.Bounds().Dx()), uint(i.Bounds().Dy()), "RGBA", imagick.PIXEL_CHAR, rgba.Pix)
|
||||
if err != nil {
|
||||
if err := mw.ConstituteImage(uint(i.Bounds().Dx()), uint(i.Bounds().Dy()), "RGBA", imagick.PIXEL_CHAR, rgba.Pix); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
|
||||
if c.Opts.Grayscale {
|
||||
_ = mw.TransformImageColorspace(imagick.COLORSPACE_GRAY)
|
||||
if err := mw.TransformImageColorspace(imagick.COLORSPACE_GRAY); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
switch filepath.Ext(fileName) {
|
||||
case ".png":
|
||||
_ = mw.SetImageFormat("PNG")
|
||||
_ = mw.WriteImage(fileName)
|
||||
if err := mw.SetImageFormat("PNG"); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.WriteImage(fileName); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
case ".tif", ".tiff":
|
||||
_ = mw.SetImageFormat("TIFF")
|
||||
_ = mw.WriteImage(fileName)
|
||||
if err := mw.SetImageFormat("TIFF"); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.WriteImage(fileName); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
case ".bmp":
|
||||
pw := imagick.NewPixelWand()
|
||||
pw.SetColor("black")
|
||||
defer pw.Destroy()
|
||||
|
||||
_ = mw.SetImageFormat("BMP3")
|
||||
_ = mw.SetImageBackgroundColor(pw)
|
||||
_ = mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE)
|
||||
_ = mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE)
|
||||
_ = mw.SetImageMatte(false)
|
||||
_ = mw.SetImageCompression(imagick.COMPRESSION_NO)
|
||||
_ = mw.QuantizeImage(16, mw.GetImageColorspace(), 1, imagick.DITHER_METHOD_FLOYD_STEINBERG, true)
|
||||
_ = mw.WriteImage(fileName)
|
||||
if err := mw.SetImageFormat("BMP3"); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageBackgroundColor(pw); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageMatte(false); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageCompression(imagick.COMPRESSION_NO); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.QuantizeImage(16, mw.GetImageColorspace(), 1, imagick.DITHER_METHOD_FLOYD_STEINBERG, true); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.WriteImage(fileName); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
case ".jpg", ".jpeg":
|
||||
_ = mw.SetImageFormat("JPEG")
|
||||
_ = mw.SetImageCompressionQuality(uint(c.Opts.Quality))
|
||||
_ = mw.WriteImage(fileName)
|
||||
if err := mw.SetImageFormat("JPEG"); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.WriteImage(fileName); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
case ".avif":
|
||||
_ = mw.SetImageFormat("AVIF")
|
||||
_ = mw.SetImageCompressionQuality(uint(c.Opts.Quality))
|
||||
_ = mw.WriteImage(fileName)
|
||||
if err := mw.SetImageFormat("AVIF"); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
if err := mw.WriteImage(fileName); err != nil {
|
||||
return fmt.Errorf("encodeIM: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// listArchive lists contents of archive.
|
||||
func (c *Convertor) listArchive(fileName string) ([]string, error) {
|
||||
var contents []string
|
||||
|
||||
archive, err := unarr.NewArchive(fileName)
|
||||
if err != nil {
|
||||
return contents, err
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
return archive.List()
|
||||
}
|
||||
|
||||
// coverArchive extracts cover from archive.
|
||||
func (c *Convertor) coverArchive(fileName string) (image.Image, error) {
|
||||
var images []string
|
||||
@@ -752,13 +786,12 @@ func (c *Convertor) coverArchive(fileName string) (image.Image, error) {
|
||||
|
||||
archive, err := unarr.NewArchive(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("coverArchive: %w", err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
err = archive.EntryFor(cover)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err = archive.EntryFor(cover); err != nil {
|
||||
return nil, fmt.Errorf("coverArchive: %w", err)
|
||||
}
|
||||
|
||||
data, err := archive.ReadAll()
|
||||
@@ -882,6 +915,7 @@ func imageToRGBA(src image.Image) *image.RGBA {
|
||||
b := src.Bounds()
|
||||
dst := image.NewRGBA(b)
|
||||
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -894,6 +928,7 @@ func imageToGray(src image.Image) *image.Gray {
|
||||
b := src.Bounds()
|
||||
dst := image.NewGray(b)
|
||||
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -905,6 +940,7 @@ func (c *Convertor) isArchive(f string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -916,6 +952,7 @@ func (c *Convertor) isDocument(f string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -927,6 +964,7 @@ func (c *Convertor) isImage(f string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -938,6 +976,7 @@ func (c *Convertor) isNonImage(f string) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -948,6 +987,7 @@ func (c *Convertor) isSize(size int64) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -957,6 +997,7 @@ func (c *Convertor) isGrayScale(img image.Image) bool {
|
||||
if model == color.GrayModel || model == color.Gray16Model {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1000,7 +1041,7 @@ func (c *Convertor) coverName(images []string) string {
|
||||
|
||||
if strings.HasPrefix(img, "cover") || strings.HasPrefix(img, "front") ||
|
||||
strings.HasSuffix(ext, "cover") || strings.HasSuffix(ext, "front") {
|
||||
return images[idx]
|
||||
return filepath.ToSlash(images[idx])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1010,7 +1051,7 @@ func (c *Convertor) coverName(images []string) string {
|
||||
for idx, img := range images {
|
||||
img = strings.ToLower(img)
|
||||
if img == cover {
|
||||
return images[idx]
|
||||
return filepath.ToSlash(images[idx])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1041,6 +1082,16 @@ func (c *Convertor) coverImage(fileName string, fileInfo os.FileInfo) (image.Ima
|
||||
return cover, nil
|
||||
}
|
||||
|
||||
// Initialize inits ImageMagick.
|
||||
func (c *Convertor) Initialize() {
|
||||
imagick.Initialize()
|
||||
}
|
||||
|
||||
// Terminate terminates ImageMagick.
|
||||
func (c *Convertor) Terminate() {
|
||||
imagick.Terminate()
|
||||
}
|
||||
|
||||
// Files returns list of found comic files.
|
||||
func (c *Convertor) Files(args []string) ([]string, error) {
|
||||
var files []string
|
||||
@@ -1076,8 +1127,7 @@ func (c *Convertor) Files(args []string) ([]string, error) {
|
||||
}
|
||||
} else {
|
||||
if c.Opts.Recursive {
|
||||
err = filepath.Walk(path, walkFiles)
|
||||
if err != nil {
|
||||
if err := filepath.Walk(path, walkFiles); err != nil {
|
||||
return files, fmt.Errorf("files: %w", err)
|
||||
}
|
||||
} else {
|
||||
@@ -1110,8 +1160,8 @@ func (c *Convertor) Files(args []string) ([]string, error) {
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// ExtractCover extracts cover.
|
||||
func (c *Convertor) ExtractCover(fileName string, fileInfo os.FileInfo) error {
|
||||
// Cover extracts cover.
|
||||
func (c *Convertor) Cover(fileName string, fileInfo os.FileInfo) error {
|
||||
c.CurrFile++
|
||||
|
||||
cover, err := c.coverImage(fileName, fileInfo)
|
||||
@@ -1127,30 +1177,27 @@ func (c *Convertor) ExtractCover(fileName string, fileInfo os.FileInfo) error {
|
||||
}
|
||||
}
|
||||
|
||||
fname := filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s.jpg", c.baseNoExt(fileName)))
|
||||
file, err := os.Create(fname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("extractCover: %w", err)
|
||||
}
|
||||
fName := filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format))
|
||||
|
||||
err = jpeg.Encode(file, cover, &jpeg.Options{Quality: c.Opts.Quality})
|
||||
if err != nil {
|
||||
return fmt.Errorf("extractCover: %w", err)
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("extractCover: %w", err)
|
||||
switch c.Opts.Format {
|
||||
case "jpeg", "png", "tiff", "webp", "avif":
|
||||
if err := c.encodeImage(cover, fName); err != nil {
|
||||
return err
|
||||
}
|
||||
case "bmp":
|
||||
if err := c.encodeIM(cover, fName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractThumbnail extracts thumbnail.
|
||||
func (c *Convertor) ExtractThumbnail(filename string, info os.FileInfo) error {
|
||||
// Thumbnail extracts thumbnail.
|
||||
func (c *Convertor) Thumbnail(fileName string, info os.FileInfo) error {
|
||||
c.CurrFile++
|
||||
|
||||
cover, err := c.coverImage(filename, info)
|
||||
cover, err := c.coverImage(fileName, info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1165,59 +1212,73 @@ func (c *Convertor) ExtractThumbnail(filename string, info os.FileInfo) error {
|
||||
cover = imaging.Resize(cover, 256, 0, filters[c.Opts.Filter])
|
||||
}
|
||||
|
||||
imagick.Initialize()
|
||||
|
||||
mw := imagick.NewMagickWand()
|
||||
defer mw.Destroy()
|
||||
|
||||
rgba := imageToRGBA(cover)
|
||||
err = mw.ConstituteImage(uint(cover.Bounds().Dx()), uint(cover.Bounds().Dy()), "RGBA", imagick.PIXEL_CHAR, rgba.Pix)
|
||||
if err != nil {
|
||||
if err := mw.ConstituteImage(uint(cover.Bounds().Dx()), uint(cover.Bounds().Dy()), "RGBA", imagick.PIXEL_CHAR, rgba.Pix); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
|
||||
var fname string
|
||||
var furi string
|
||||
var fName string
|
||||
var fUri string
|
||||
|
||||
if c.Opts.Outfile == "" {
|
||||
furi = "file://" + filename
|
||||
fname = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%x.png", md5.Sum([]byte(furi))))
|
||||
fUri = "file://" + fileName
|
||||
fName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%x.png", md5.Sum([]byte(fUri))))
|
||||
} else {
|
||||
abs, _ := filepath.Abs(c.Opts.Outfile)
|
||||
furi = "file://" + abs
|
||||
fname = abs
|
||||
fUri = "file://" + abs
|
||||
fName = abs
|
||||
}
|
||||
|
||||
err = mw.SetImageFormat("PNG")
|
||||
if err != nil {
|
||||
if err := mw.SetImageFormat("PNG"); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
err = mw.SetImageProperty("Software", "CBconvert")
|
||||
if err != nil {
|
||||
if err := mw.SetImageProperty("Software", "CBconvert"); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
err = mw.SetImageProperty("Description", "Thumbnail of "+furi)
|
||||
if err != nil {
|
||||
if err := mw.SetImageProperty("Description", "Thumbnail of "+fUri); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
err = mw.SetImageProperty("Thumb::URI", furi)
|
||||
if err != nil {
|
||||
if err := mw.SetImageProperty("Thumb::URI", fUri); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
err = mw.SetImageProperty("Thumb::MTime", strconv.FormatInt(info.ModTime().Unix(), 10))
|
||||
if err != nil {
|
||||
if err := mw.SetImageProperty("Thumb::MTime", strconv.FormatInt(info.ModTime().Unix(), 10)); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
err = mw.SetImageProperty("Thumb::Size", strconv.FormatInt(info.Size(), 10))
|
||||
if err != nil {
|
||||
if err := mw.SetImageProperty("Thumb::Size", strconv.FormatInt(info.Size(), 10)); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
err = mw.SetImageProperty("Thumb::Mimetype", mime.TypeByExtension(filepath.Ext(filename)))
|
||||
if err != nil {
|
||||
if err := mw.SetImageProperty("Thumb::Mimetype", mime.TypeByExtension(filepath.Ext(fileName))); err != nil {
|
||||
return fmt.Errorf("extractThumbnail: %w", err)
|
||||
}
|
||||
|
||||
return mw.WriteImage(fname)
|
||||
return mw.WriteImage(fName)
|
||||
}
|
||||
|
||||
// Meta manipulates with CBZ metadata.
|
||||
func (c *Convertor) Meta(fileName string, info os.FileInfo) (any, error) {
|
||||
c.CurrFile++
|
||||
|
||||
if c.Opts.Cover {
|
||||
var images []string
|
||||
|
||||
contents, err := c.listArchive(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, ct := range contents {
|
||||
if c.isImage(ct) {
|
||||
images = append(images, ct)
|
||||
}
|
||||
}
|
||||
|
||||
return c.coverName(images), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Convert converts comic book.
|
||||
@@ -1225,30 +1286,24 @@ func (c *Convertor) Convert(filename string, info os.FileInfo) error {
|
||||
c.CurrFile++
|
||||
|
||||
if info.IsDir() {
|
||||
err := c.convertDirectory(filename)
|
||||
if err != nil {
|
||||
if err := c.convertDirectory(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.saveArchive(filename)
|
||||
if err != nil {
|
||||
if err := c.saveArchive(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if c.isDocument(filename) {
|
||||
err := c.convertDocument(filename)
|
||||
if err != nil {
|
||||
if err := c.convertDocument(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.saveArchive(filename)
|
||||
if err != nil {
|
||||
if err := c.saveArchive(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := c.convertArchive(filename)
|
||||
if err != nil {
|
||||
if err := c.convertArchive(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.saveArchive(filename)
|
||||
if err != nil {
|
||||
if err := c.saveArchive(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+10
-8
@@ -2,8 +2,10 @@ module github.com/gen2brain/cbconvert/cmd/cbconvert
|
||||
|
||||
go 1.19
|
||||
|
||||
//replace github.com/gen2brain/cbconvert => ../../
|
||||
|
||||
require (
|
||||
github.com/gen2brain/cbconvert v0.0.0-20220909080114-0963cb176280
|
||||
github.com/gen2brain/cbconvert v0.0.0-20230602085208-16d01e292edd
|
||||
github.com/schollz/progressbar/v3 v3.10.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
)
|
||||
@@ -11,17 +13,17 @@ require (
|
||||
require (
|
||||
github.com/chai2010/webp v1.1.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.2 // indirect
|
||||
github.com/gen2brain/go-fitz v1.20.2 // indirect
|
||||
github.com/gen2brain/go-unarr v0.1.6 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/gen2brain/go-fitz v1.22.2 // indirect
|
||||
github.com/gen2brain/go-unarr v0.1.7 // indirect
|
||||
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
github.com/strukturag/libheif v1.13.0 // indirect
|
||||
golang.org/x/image v0.2.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
|
||||
golang.org/x/image v0.7.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/term v0.5.0 // indirect
|
||||
gopkg.in/gographics/imagick.v3 v3.4.2 // indirect
|
||||
)
|
||||
|
||||
+21
-14
@@ -5,14 +5,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
|
||||
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/gen2brain/cbconvert v0.0.0-20220909080114-0963cb176280 h1:wtlm/hA4mVv/CcUCm3gWGOH5oA3qNodLh7zoGjQO7cA=
|
||||
github.com/gen2brain/cbconvert v0.0.0-20220909080114-0963cb176280/go.mod h1:GO5h+s7160FK66rth/0QS06HgXFwE57jaP3OTX/8Rv0=
|
||||
github.com/gen2brain/go-fitz v1.20.2 h1:4FPJCU/ImQ32oojBsYn/+oTkRORxbAhAA+Yw1Fm97MA=
|
||||
github.com/gen2brain/go-fitz v1.20.2/go.mod h1:YbQPODTC/UnQ/RK4JyD3zfpDQ19UKiV85nMMT3XpT0s=
|
||||
github.com/gen2brain/go-unarr v0.1.6 h1:2TtfIQ2dGuCkgEYa+vPE1ydcpkB3CtBbdYMfRSGLdA8=
|
||||
github.com/gen2brain/go-unarr v0.1.6/go.mod h1:P05CsEe8jVEXhxqXqp9mFKUKFV0BKpFmtgNWf8Mcoos=
|
||||
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/gen2brain/cbconvert v0.0.0-20230602085208-16d01e292edd h1:vfoiB9k/XWPVwCK5Wu0F5U60L5zqDmdZO5br7tDGITw=
|
||||
github.com/gen2brain/cbconvert v0.0.0-20230602085208-16d01e292edd/go.mod h1:xxmaVGM48oWMKNp0Dwlb7XwN2dWx6Jp4DkjNuaTrAOo=
|
||||
github.com/gen2brain/go-fitz v1.22.2 h1:pisRYS3x/tvsiS4UzdBoiStmOxYoisOGNCUB4+0RKhE=
|
||||
github.com/gen2brain/go-fitz v1.22.2/go.mod h1:HU04vc+RisUh/kvEd2pB0LAxmK1oyXdN4ftyshUr9rQ=
|
||||
github.com/gen2brain/go-unarr v0.1.7 h1:mEE7bPShJIsmAX67t6BW2ibpEUO7j5WK152KgNM9NbQ=
|
||||
github.com/gen2brain/go-unarr v0.1.7/go.mod h1:MK9a3hddpaIxjEtrE1f/LA5yJ7gA34cS7Oyr325sY9s=
|
||||
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 h1:NlUATi3cllRJhpM4mfR9BxiLRXT83bcSLcOa+S8lrME=
|
||||
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7/go.mod h1:Hku3FQ2laCEwSv7Z8YkC0er38jLaUycUCbsFkWMr+z4=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
@@ -39,35 +39,42 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
|
||||
golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY=
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.2 h1:vk6oildvhRBVSBfQ4X3raJstApYSeK6CZsyzoSOZk58=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.2/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
|
||||
|
||||
+36
-11
@@ -21,8 +21,7 @@ func main() {
|
||||
go func() {
|
||||
for range c {
|
||||
fmt.Println("\naborting")
|
||||
err := os.RemoveAll(conv.Workdir)
|
||||
if err != nil {
|
||||
if err := os.RemoveAll(conv.Workdir); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
@@ -30,13 +29,15 @@ func main() {
|
||||
}()
|
||||
|
||||
if _, err := os.Stat(opts.Outdir); err != nil {
|
||||
err = os.MkdirAll(opts.Outdir, 0775)
|
||||
if err != nil {
|
||||
if err := os.MkdirAll(opts.Outdir, 0775); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conv.Initialize()
|
||||
defer conv.Terminate()
|
||||
|
||||
files, err := conv.Files(args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -44,7 +45,7 @@ func main() {
|
||||
}
|
||||
|
||||
var bar *progressbar.ProgressBar
|
||||
if opts.Cover || opts.Thumbnail {
|
||||
if opts.Cover || opts.Thumbnail || opts.Meta {
|
||||
if !opts.Quiet {
|
||||
bar = progressbar.NewOptions(conv.Nfiles,
|
||||
progressbar.OptionShowCount(),
|
||||
@@ -86,24 +87,33 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.Cover {
|
||||
err = conv.ExtractCover(file, stat)
|
||||
if opts.Meta {
|
||||
ret, err := conv.Meta(file, stat)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if opts.Cover {
|
||||
fmt.Println(ret)
|
||||
}
|
||||
|
||||
continue
|
||||
} else if opts.Cover {
|
||||
if err := conv.Cover(file, stat); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
continue
|
||||
} else if opts.Thumbnail {
|
||||
err = conv.ExtractThumbnail(file, stat)
|
||||
if err != nil {
|
||||
if err = conv.Thumbnail(file, stat); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
err = conv.Convert(file, stat)
|
||||
if err != nil {
|
||||
if err := conv.Convert(file, stat); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -149,6 +159,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
cover.IntVar(&opts.Width, "width", 0, "Image width")
|
||||
cover.IntVar(&opts.Height, "height", 0, "Image height")
|
||||
cover.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||
cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
||||
cover.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||
cover.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
||||
cover.StringVar(&opts.Outdir, "outdir", ".", "Output directory")
|
||||
@@ -168,6 +179,10 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
thumbnail.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively")
|
||||
thumbnail.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")
|
||||
|
||||
meta := flag.NewFlagSet("meta", flag.ExitOnError)
|
||||
meta.SortFlags = false
|
||||
meta.BoolVar(&opts.Cover, "cover", false, "Print cover name")
|
||||
|
||||
convert.Usage = func() {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Usage: %s <command> [<flags>] [file1 dir1 ... fileOrDirN]\n\n", filepath.Base(os.Args[0]))
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\nCommands:\n")
|
||||
@@ -189,6 +204,12 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\n \t")
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||
})
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\n meta\n \tCBZ metadata\n\n")
|
||||
meta.VisitAll(func(f *flag.Flag) {
|
||||
_, _ = fmt.Fprintf(os.Stderr, " --%s", f.Name)
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\n \t")
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||
})
|
||||
_, _ = fmt.Fprintf(os.Stderr, "\n")
|
||||
}
|
||||
|
||||
@@ -210,6 +231,10 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
opts.Thumbnail = true
|
||||
_ = thumbnail.Parse(os.Args[2:])
|
||||
args = thumbnail.Args()
|
||||
case "meta":
|
||||
opts.Meta = true
|
||||
_ = meta.Parse(os.Args[2:])
|
||||
args = meta.Args()
|
||||
default:
|
||||
_ = convert.Parse(os.Args[1:])
|
||||
args = convert.Args()
|
||||
|
||||
+19
-6
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GLIBC_x86_64="/usr/x86_64-pc-linux-gnu-static"
|
||||
MUSL_x86_64="/usr/x86_64-pc-linux-musl"
|
||||
MUSL_aarch64="/usr/aarch64-pc-linux-musl"
|
||||
MINGW_x86_64="/usr/x86_64-w64-mingw32"
|
||||
@@ -9,17 +10,29 @@ MACOS_aarch64="/usr/aarch64-apple-darwin"
|
||||
VERSION="`git --git-dir ../../.git describe --tags --abbrev=0 2>/dev/null || echo '0.0.0'`"
|
||||
|
||||
BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
|
||||
CC=x86_64-pc-linux-musl-gcc \
|
||||
PKG_CONFIG="x86_64-pc-linux-musl-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MUSL_x86_64/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MUSL_x86_64/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MUSL_x86_64/usr/include" \
|
||||
CGO_LDFLAGS="-L$MUSL_x86_64/usr/lib" \
|
||||
CC=x86_64-pc-linux-gnu-gcc \
|
||||
PKG_CONFIG="x86_64-pc-linux-gnu-pkg-config" \
|
||||
PKG_CONFIG_PATH="$GLIBC_x86_64/usr/lib64/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$GLIBC_x86_64/usr/lib64/pkgconfig" \
|
||||
CGO_CFLAGS="-I$GLIBC_x86_64/usr/include" \
|
||||
CGO_LDFLAGS="-L$GLIBC_x86_64/usr/lib64" \
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w '-extldflags=-static'" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && tar -czf "${BUILDDIR}-linux-x86_64.tar.gz" ${BUILDDIR}
|
||||
rm -rf ${BUILDDIR}
|
||||
|
||||
#BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
|
||||
#CC=x86_64-pc-linux-musl-gcc \
|
||||
#PKG_CONFIG="x86_64-pc-linux-musl-pkg-config" \
|
||||
#PKG_CONFIG_PATH="$MUSL_x86_64/usr/lib/pkgconfig" \
|
||||
#PKG_CONFIG_LIBDIR="$MUSL_x86_64/usr/lib/pkgconfig" \
|
||||
#CGO_CFLAGS="-I$MUSL_x86_64/usr/include" \
|
||||
#CGO_LDFLAGS="-L$MUSL_x86_64/usr/lib" \
|
||||
#CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||
#go build -trimpath -tags 'extlib pkgconfig' -v -o ${BUILDDIR}/cbconvert -ldflags "-linkmode external -s -w '-extldflags=-static'" && \
|
||||
#cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && tar -czf "${BUILDDIR}-linux-x86_64.tar.gz" ${BUILDDIR}
|
||||
#rm -rf ${BUILDDIR}
|
||||
|
||||
BUILDDIR="cbconvert-${VERSION}"; mkdir -p ${BUILDDIR}
|
||||
CC=aarch64-pc-linux-musl-gcc \
|
||||
PKG_CONFIG="aarch64-pc-linux-musl-pkg-config" \
|
||||
|
||||
@@ -5,12 +5,12 @@ go 1.19
|
||||
require (
|
||||
github.com/chai2010/webp v1.1.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/fvbommel/sortorder v1.0.2
|
||||
github.com/gen2brain/go-fitz v1.20.1
|
||||
github.com/gen2brain/go-unarr v0.1.6
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/gen2brain/go-fitz v1.22.2
|
||||
github.com/gen2brain/go-unarr v0.1.7
|
||||
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7
|
||||
github.com/strukturag/libheif v1.13.0
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
|
||||
gopkg.in/gographics/imagick.v3 v3.4.1
|
||||
golang.org/x/image v0.7.0
|
||||
golang.org/x/sync v0.2.0
|
||||
gopkg.in/gographics/imagick.v3 v3.4.2
|
||||
)
|
||||
|
||||
@@ -2,23 +2,51 @@ github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
|
||||
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/fvbommel/sortorder v1.0.2 h1:mV4o8B2hKboCdkJm+a7uX/SIpZob4JzUpc5GGnM45eo=
|
||||
github.com/fvbommel/sortorder v1.0.2/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/gen2brain/go-fitz v1.20.1 h1:i5GPe/58q/gbNqa2mO+ZcTymwowbJEsDOXp7D0JwxgU=
|
||||
github.com/gen2brain/go-fitz v1.20.1/go.mod h1:UZAxMETTDK4UPpuh80HaRpPzgkSibUihXVzwj2ip5oQ=
|
||||
github.com/gen2brain/go-unarr v0.1.6 h1:2TtfIQ2dGuCkgEYa+vPE1ydcpkB3CtBbdYMfRSGLdA8=
|
||||
github.com/gen2brain/go-unarr v0.1.6/go.mod h1:P05CsEe8jVEXhxqXqp9mFKUKFV0BKpFmtgNWf8Mcoos=
|
||||
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/gen2brain/go-fitz v1.22.2 h1:pisRYS3x/tvsiS4UzdBoiStmOxYoisOGNCUB4+0RKhE=
|
||||
github.com/gen2brain/go-fitz v1.22.2/go.mod h1:HU04vc+RisUh/kvEd2pB0LAxmK1oyXdN4ftyshUr9rQ=
|
||||
github.com/gen2brain/go-unarr v0.1.7 h1:mEE7bPShJIsmAX67t6BW2ibpEUO7j5WK152KgNM9NbQ=
|
||||
github.com/gen2brain/go-unarr v0.1.7/go.mod h1:MK9a3hddpaIxjEtrE1f/LA5yJ7gA34cS7Oyr325sY9s=
|
||||
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 h1:NlUATi3cllRJhpM4mfR9BxiLRXT83bcSLcOa+S8lrME=
|
||||
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7/go.mod h1:Hku3FQ2laCEwSv7Z8YkC0er38jLaUycUCbsFkWMr+z4=
|
||||
github.com/strukturag/libheif v1.13.0 h1:SuLo/Fl/Nzbw0ixOya1YZSl0Xd27X4fgofGnJdvOHqI=
|
||||
github.com/strukturag/libheif v1.13.0/go.mod h1:E/PNRlmVtrtj9j2AvBZlrO4dsBDu6KfwDZn7X1Ce8Ks=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw=
|
||||
golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.1 h1:CClNBnd1UGxH9KAl4Vuwx+jgNRkyKN+cHlbuFPyt+KU=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.1/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.2 h1:vk6oildvhRBVSBfQ4X3raJstApYSeK6CZsyzoSOZk58=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.2/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
|
||||
|
||||
Reference in New Issue
Block a user