mirror of
https://github.com/gen2brain/cbconvert
synced 2025-10-13 18:18:52 +02:00
Add support for JXL
This commit is contained in:
84
.github/workflows/test.yml
vendored
84
.github/workflows/test.yml
vendored
@@ -10,23 +10,9 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cmake-version: '3.27.x'
|
cmake-version: '3.27.x'
|
||||||
|
|
||||||
- name: Wget libaom
|
- name: Update apt-get
|
||||||
uses: wei/wget@v1
|
|
||||||
with:
|
|
||||||
args: https://storage.googleapis.com/aom-releases/libaom-3.7.0.tar.gz
|
|
||||||
- name: Unpack libaom
|
|
||||||
run: |
|
run: |
|
||||||
tar -xpf libaom-3.7.0.tar.gz && mkdir build
|
sudo apt-get update
|
||||||
- name: Configure libaom
|
|
||||||
working-directory: build
|
|
||||||
run: |
|
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_DOCS=OFF \
|
|
||||||
-DENABLE_EXAMPLES=OFF -DENABLE_NASM=OFF -DENABLE_TESTS=OFF -DENABLE_TOOLS=OFF -DENABLE_WERROR=OFF \
|
|
||||||
-DAOM_TARGET_CPU=generic ../libaom-3.7.0
|
|
||||||
- name: Install libaom
|
|
||||||
working-directory: build
|
|
||||||
run: |
|
|
||||||
make -j3 && sudo make install
|
|
||||||
|
|
||||||
- name: Wget libheif
|
- name: Wget libheif
|
||||||
uses: wei/wget@v1
|
uses: wei/wget@v1
|
||||||
@@ -35,16 +21,72 @@ jobs:
|
|||||||
- name: Unpack libheif
|
- name: Unpack libheif
|
||||||
run: |
|
run: |
|
||||||
tar -xpf libheif-1.15.2.tar.gz
|
tar -xpf libheif-1.15.2.tar.gz
|
||||||
|
- name: Install libheif dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install libaom-dev -y
|
||||||
- name: Configure libheif
|
- name: Configure libheif
|
||||||
working-directory: libheif-1.15.2
|
working-directory: libheif-1.15.2
|
||||||
run: |
|
run: |
|
||||||
./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --enable-shared --disable-static --disable-libde265 \
|
./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --enable-shared --disable-static --disable-libde265 \
|
||||||
--disable-dav1d --disable-go --enable-aom --disable-gdk-pixbuf --disable-rav1e --disable-tests --disable-x265
|
--disable-dav1d --disable-go --enable-aom --disable-gdk-pixbuf --disable-rav1e --disable-tests --disable-x265 --disable-examples
|
||||||
- name: Install libheif
|
- name: Install libheif
|
||||||
working-directory: libheif-1.15.2
|
working-directory: libheif-1.15.2
|
||||||
run: |
|
run: |
|
||||||
make -j3 && sudo make install
|
make -j3 && sudo make install
|
||||||
|
|
||||||
|
- name: Wget lcms2
|
||||||
|
uses: wei/wget@v1
|
||||||
|
with:
|
||||||
|
args: https://github.com/mm2/Little-CMS/releases/download/lcms2.15/lcms2-2.15.tar.gz
|
||||||
|
- name: Unpack lcms2
|
||||||
|
run: |
|
||||||
|
tar -xpf lcms2-2.15.tar.gz
|
||||||
|
- name: Configure lcms2
|
||||||
|
working-directory: lcms2-2.15
|
||||||
|
run: |
|
||||||
|
./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --enable-shared --disable-static
|
||||||
|
- name: Install lcms2
|
||||||
|
working-directory: lcms2-2.15
|
||||||
|
run: |
|
||||||
|
make -j3 && sudo make install
|
||||||
|
|
||||||
|
- name: Wget highway
|
||||||
|
uses: wei/wget@v1
|
||||||
|
with:
|
||||||
|
args: -O highway-1.0.5.tar.gz https://github.com/google/highway/archive/refs/tags/1.0.5.tar.gz
|
||||||
|
- name: Unpack highway
|
||||||
|
run: |
|
||||||
|
tar -xpf highway-1.0.5.tar.gz && mkdir -p highway-1.0.5/build
|
||||||
|
- name: Configure highway
|
||||||
|
working-directory: highway-1.0.5/build
|
||||||
|
run: |
|
||||||
|
cmake -DCMAKE_INSTALL_PREFIX=/usr -DHWY_ENABLE_TESTS=OFF -DHWY_ENABLE_EXAMPLES=OFF -DHWY_WARNINGS_ARE_ERRORS=OFF ../
|
||||||
|
- name: Install highway
|
||||||
|
working-directory: highway-1.0.5/build
|
||||||
|
run: |
|
||||||
|
make -j3 && sudo make install
|
||||||
|
|
||||||
|
- name: Wget libjxl
|
||||||
|
uses: wei/wget@v1
|
||||||
|
with:
|
||||||
|
args: -O libjxl-0.8.2.tar.gz https://github.com/libjxl/libjxl/archive/refs/tags/v0.8.2.tar.gz
|
||||||
|
- name: Unpack libjxl
|
||||||
|
run: |
|
||||||
|
tar -xpf libjxl-0.8.2.tar.gz && mkdir -p libjxl-0.8.2/build
|
||||||
|
- name: Configure libjxl
|
||||||
|
working-directory: libjxl-0.8.2/build
|
||||||
|
run: |
|
||||||
|
cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DJPEGXL_ENABLE_BENCHMARK=OFF \
|
||||||
|
-DJPEGXL_ENABLE_COVERAGE=OFF -DJPEGXL_ENABLE_FUZZERS=OFF -DJPEGXL_ENABLE_SJPEG=OFF -DJPEGXL_WARNINGS_AS_ERRORS=OFF \
|
||||||
|
-DJPEGXL_ENABLE_SKCMS=OFF -DJPEGXL_ENABLE_VIEWERS=OFF -DJPEGXL_ENABLE_PLUGINS=OFF -DJPEGXL_ENABLE_DOXYGEN=OFF \
|
||||||
|
-DJPEGXL_ENABLE_MANPAGES=OFF -DJPEGXL_ENABLE_JNI=OFF -DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF -DJPEGXL_ENABLE_TCMALLOC=OFF \
|
||||||
|
-DJPEGXL_ENABLE_EXAMPLES=OFF -DJPEGXL_ENABLE_TOOLS=OFF -DJPEGXL_ENABLE_OPENEXR=OFF -DBUILD_TESTING=OFF \
|
||||||
|
-DJXL_HWY_DISABLED_TARGETS_FORCED=ON -DJPEGXL_FORCE_SYSTEM_BROTLI=ON -DJPEGXL_FORCE_SYSTEM_HWY=ON ../
|
||||||
|
- name: Install libjxl
|
||||||
|
working-directory: libjxl-0.8.2/build
|
||||||
|
run: |
|
||||||
|
make -j3 && sudo make install
|
||||||
|
|
||||||
- name: Wget ImageMagick
|
- name: Wget ImageMagick
|
||||||
uses: wei/wget@v1
|
uses: wei/wget@v1
|
||||||
with:
|
with:
|
||||||
@@ -59,20 +101,20 @@ jobs:
|
|||||||
--without-frozenpaths --without-utilities --disable-hdri --disable-opencl --without-modules --without-magick-plus-plus --without-perl \
|
--without-frozenpaths --without-utilities --disable-hdri --disable-opencl --without-modules --without-magick-plus-plus --without-perl \
|
||||||
--without-bzlib --without-x --without-zip --with-zlib --without-dps --without-djvu --without-autotrace --without-fftw \
|
--without-bzlib --without-x --without-zip --with-zlib --without-dps --without-djvu --without-autotrace --without-fftw \
|
||||||
--without-fpx --without-fontconfig --without-freetype --without-gslib --without-gvc --without-jbig --without-openjp2 \
|
--without-fpx --without-fontconfig --without-freetype --without-gslib --without-gvc --without-jbig --without-openjp2 \
|
||||||
--without-jxl --without-lcms --without-lqr --without-lzma --without-openexr --without-pango --without-raw \
|
--without-lcms --without-lqr --without-lzma --without-openexr --without-pango --without-raw --without-rsvg --without-wmf \
|
||||||
--without-rsvg --without-wmf --without-xml --disable-openmp --with-jpeg --with-heic --with-png --with-tiff --with-webp
|
--without-xml --disable-openmp --with-jpeg --with-heic --with-jxl --with-png --with-tiff --with-webp
|
||||||
- name: Install ImageMagick
|
- name: Install ImageMagick
|
||||||
working-directory: ImageMagick-7.1.1-15
|
working-directory: ImageMagick-7.1.1-15
|
||||||
run: |
|
run: |
|
||||||
make -j3 && sudo make install
|
make -j3 && sudo make install
|
||||||
|
|
||||||
- name: Install Go
|
- name: Install Go
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: 1.21
|
go-version: 1.21
|
||||||
|
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test
|
run: go test
|
||||||
|
10
README.md
10
README.md
@@ -10,7 +10,7 @@ It can convert comics to different formats to fit your various devices.
|
|||||||
|
|
||||||
* reads CBR (RAR), CBZ (ZIP), CB7 (7Z), CBT (TAR), PDF, XPS, EPUB, MOBI and plain directory
|
* reads CBR (RAR), CBZ (ZIP), CB7 (7Z), CBT (TAR), PDF, XPS, EPUB, MOBI and plain directory
|
||||||
* saves processed comics in ZIP archive format or TAR
|
* saves processed comics in ZIP archive format or TAR
|
||||||
* images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, or 4-Bit BMP (16 colors) file format
|
* images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, JXL, or 4-Bit BMP (16 colors) file format
|
||||||
* rotate, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images
|
* rotate, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images
|
||||||
* resize filters (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
|
* resize filters (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
|
||||||
* export covers from comics
|
* export covers from comics
|
||||||
@@ -58,13 +58,11 @@ This is what it looks like in the PCManFM file manager:
|
|||||||
--fit
|
--fit
|
||||||
Best fit for required width and height (default "false")
|
Best fit for required width and height (default "false")
|
||||||
--format
|
--format
|
||||||
Image format, valid values are jpeg, png, tiff, bmp, webp, avif (default "jpeg")
|
Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl (default "jpeg")
|
||||||
--archive
|
--archive
|
||||||
Archive format, valid values are zip, tar (default "zip")
|
Archive format, valid values are zip, tar (default "zip")
|
||||||
--quality
|
--quality
|
||||||
Image quality (default "75")
|
Image quality (default "75")
|
||||||
--lossless
|
|
||||||
Lossless compression (avif) (default "false")
|
|
||||||
--filter
|
--filter
|
||||||
0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
|
0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
|
||||||
--no-cover
|
--no-cover
|
||||||
@@ -114,7 +112,7 @@ This is what it looks like in the PCManFM file manager:
|
|||||||
--fit
|
--fit
|
||||||
Best fit for required width and height (default "false")
|
Best fit for required width and height (default "false")
|
||||||
--format
|
--format
|
||||||
Image format, valid values are jpeg, png, tiff, bmp, webp, avif (default "jpeg")
|
Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl (default "jpeg")
|
||||||
--quality
|
--quality
|
||||||
Image quality (default "75")
|
Image quality (default "75")
|
||||||
--filter
|
--filter
|
||||||
@@ -198,6 +196,6 @@ This table maps quality settings for JPEG to the respective AVIF and WEBP qualit
|
|||||||
|
|
||||||
### Compile
|
### Compile
|
||||||
|
|
||||||
Install ImageMagick7, MuPDF and libheif (with libaom) libraries and headers and then install to GOBIN:
|
Install ImageMagick7 (with libheif/libjxl support) and MuPDF libraries and headers and then install to GOBIN:
|
||||||
|
|
||||||
`go install -tags extlib github.com/gen2brain/cbconvert/cmd/cbconvert@latest`
|
`go install -tags extlib github.com/gen2brain/cbconvert/cmd/cbconvert@latest`
|
||||||
|
72
cbconvert.go
72
cbconvert.go
@@ -27,7 +27,6 @@ import (
|
|||||||
"image/png"
|
"image/png"
|
||||||
|
|
||||||
"github.com/chai2010/webp"
|
"github.com/chai2010/webp"
|
||||||
"github.com/strukturag/libheif/go/heif"
|
|
||||||
"golang.org/x/image/tiff"
|
"golang.org/x/image/tiff"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
@@ -69,14 +68,12 @@ var filters = map[int]imaging.ResampleFilter{
|
|||||||
|
|
||||||
// Options type.
|
// Options type.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// Image format, valid values are jpeg, png, tiff, bmp, webp, avif
|
// Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl
|
||||||
Format string
|
Format string
|
||||||
// Archive format, valid values are zip, tar
|
// Archive format, valid values are zip, tar
|
||||||
Archive string
|
Archive string
|
||||||
// JPEG image quality
|
// JPEG image quality
|
||||||
Quality int
|
Quality int
|
||||||
// Lossless compression (avif)
|
|
||||||
Lossless bool
|
|
||||||
// Image width
|
// Image width
|
||||||
Width int
|
Width int
|
||||||
// Image height
|
// Image height
|
||||||
@@ -179,6 +176,20 @@ type Image struct {
|
|||||||
SizeHuman string
|
SizeHuman string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewOptions returns default options.
|
||||||
|
func NewOptions() Options {
|
||||||
|
o := Options{}
|
||||||
|
o.Format = "jpeg"
|
||||||
|
o.Archive = "zip"
|
||||||
|
o.Quality = 75
|
||||||
|
o.Filter = 2
|
||||||
|
o.LevelsGamma = 1.0
|
||||||
|
o.LevelsInMax = 255
|
||||||
|
o.LevelsOutMax = 255
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
// New returns new convertor.
|
// New returns new convertor.
|
||||||
func New(o Options) *Convertor {
|
func New(o Options) *Convertor {
|
||||||
c := &Convertor{}
|
c := &Convertor{}
|
||||||
@@ -479,12 +490,11 @@ func (c *Convertor) imageConvert(ctx context.Context, img image.Image, index int
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch c.Opts.Format {
|
switch c.Opts.Format {
|
||||||
case "jpeg", "png", "tiff", "webp", "avif":
|
case "jpeg", "png", "tiff", "webp":
|
||||||
if err := c.imageEncode(img, fileName); err != nil {
|
if err := c.imageEncode(img, fileName); err != nil {
|
||||||
return fmt.Errorf("imageConvert: %w", err)
|
return fmt.Errorf("imageConvert: %w", err)
|
||||||
}
|
}
|
||||||
case "bmp":
|
case "bmp", "avif", "jxl":
|
||||||
// convert image to 4-Bit BMP (16 colors)
|
|
||||||
if err := c.imEncode(img, fileName); err != nil {
|
if err := c.imEncode(img, fileName); err != nil {
|
||||||
return fmt.Errorf("imageConvert: %w", err)
|
return fmt.Errorf("imageConvert: %w", err)
|
||||||
}
|
}
|
||||||
@@ -643,18 +653,6 @@ func (c *Convertor) imageEncode(img image.Image, fileName string) error {
|
|||||||
err = jpeg.Encode(file, img, &jpeg.Options{Quality: c.Opts.Quality})
|
err = jpeg.Encode(file, img, &jpeg.Options{Quality: c.Opts.Quality})
|
||||||
case ".webp":
|
case ".webp":
|
||||||
err = webp.Encode(file, img, &webp.Options{Quality: float32(c.Opts.Quality)})
|
err = webp.Encode(file, img, &webp.Options{Quality: float32(c.Opts.Quality)})
|
||||||
case ".avif":
|
|
||||||
img = imageToRGBA(img)
|
|
||||||
lossLess := heif.LosslessModeDisabled
|
|
||||||
if c.Opts.Lossless {
|
|
||||||
lossLess = heif.LosslessModeEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, e := heif.EncodeFromImage(img, heif.CompressionAV1, c.Opts.Quality, lossLess, 0)
|
|
||||||
if e != nil {
|
|
||||||
return fmt.Errorf("imageEncode: %w", e)
|
|
||||||
}
|
|
||||||
err = ctx.WriteToFile(fileName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -680,16 +678,10 @@ func (c *Convertor) imEncode(i image.Image, fileName string) error {
|
|||||||
if err := mw.SetImageFormat("PNG"); err != nil {
|
if err := mw.SetImageFormat("PNG"); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
if err := mw.WriteImage(fileName); err != nil {
|
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
|
||||||
}
|
|
||||||
case ".tif", ".tiff":
|
case ".tif", ".tiff":
|
||||||
if err := mw.SetImageFormat("TIFF"); err != nil {
|
if err := mw.SetImageFormat("TIFF"); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
if err := mw.WriteImage(fileName); err != nil {
|
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
|
||||||
}
|
|
||||||
case ".bmp":
|
case ".bmp":
|
||||||
pw := imagick.NewPixelWand()
|
pw := imagick.NewPixelWand()
|
||||||
pw.SetColor("black")
|
pw.SetColor("black")
|
||||||
@@ -713,10 +705,7 @@ func (c *Convertor) imEncode(i image.Image, fileName string) error {
|
|||||||
if err := mw.SetImageCompression(imagick.COMPRESSION_NO); err != nil {
|
if err := mw.SetImageCompression(imagick.COMPRESSION_NO); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
if err := mw.QuantizeImage(16, mw.GetImageColorspace(), 1, imagick.DITHER_METHOD_FLOYD_STEINBERG, true); err != nil {
|
if err := mw.QuantizeImage(16, mw.GetImageColorspace(), 1, imagick.DITHER_METHOD_NO, true); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
|
||||||
}
|
|
||||||
if err := mw.WriteImage(fileName); err != nil {
|
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
case ".jpg", ".jpeg":
|
case ".jpg", ".jpeg":
|
||||||
@@ -726,9 +715,6 @@ func (c *Convertor) imEncode(i image.Image, fileName string) error {
|
|||||||
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
if err := mw.WriteImage(fileName); err != nil {
|
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
|
||||||
}
|
|
||||||
case ".avif":
|
case ".avif":
|
||||||
if err := mw.SetImageFormat("AVIF"); err != nil {
|
if err := mw.SetImageFormat("AVIF"); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
@@ -736,9 +722,17 @@ func (c *Convertor) imEncode(i image.Image, fileName string) error {
|
|||||||
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
if err := mw.WriteImage(fileName); err != nil {
|
case ".jxl":
|
||||||
|
if err := mw.SetImageFormat("JXL"); err != nil {
|
||||||
return fmt.Errorf("imEncode: %w", err)
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
|
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
|
||||||
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mw.WriteImage(fileName); err != nil {
|
||||||
|
return fmt.Errorf("imEncode: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -1409,7 +1403,7 @@ func (c *Convertor) isDocument(f string) bool {
|
|||||||
|
|
||||||
// isImage checks if file is image.
|
// isImage checks if file is image.
|
||||||
func (c *Convertor) isImage(f string) bool {
|
func (c *Convertor) isImage(f string) bool {
|
||||||
var types = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".avif"}
|
var types = []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".tif", ".webp", ".avif", ".jxl"}
|
||||||
for _, t := range types {
|
for _, t := range types {
|
||||||
if strings.ToLower(filepath.Ext(f)) == t {
|
if strings.ToLower(filepath.Ext(f)) == t {
|
||||||
return true
|
return true
|
||||||
@@ -1530,7 +1524,7 @@ func (c *Convertor) Files(args []string) ([]File, error) {
|
|||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
for _, fn := range fs {
|
for _, fn := range fs {
|
||||||
if !fn.IsDir() {
|
if !fn.IsDir() && c.isImage(fn.Name()) {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1632,11 +1626,11 @@ func (c *Convertor) Cover(fileName string, fileInfo os.FileInfo) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch c.Opts.Format {
|
switch c.Opts.Format {
|
||||||
case "jpeg", "png", "tiff", "webp", "avif":
|
case "jpeg", "png", "tiff", "webp":
|
||||||
if err := c.imageEncode(cover, fName); err != nil {
|
if err := c.imageEncode(cover, fName); err != nil {
|
||||||
return fmt.Errorf("%s: %w", fileName, err)
|
return fmt.Errorf("%s: %w", fileName, err)
|
||||||
}
|
}
|
||||||
case "bmp":
|
case "bmp", "avif", "jxl":
|
||||||
if err := c.imEncode(cover, fName); err != nil {
|
if err := c.imEncode(cover, fName); err != nil {
|
||||||
return fmt.Errorf("%s: %w", fileName, err)
|
return fmt.Errorf("%s: %w", fileName, err)
|
||||||
}
|
}
|
||||||
@@ -1791,11 +1785,11 @@ func (c *Convertor) Preview(fileName string, fileInfo os.FileInfo, width, height
|
|||||||
tmpName := c.tempName("cbc", "."+c.Opts.Format)
|
tmpName := c.tempName("cbc", "."+c.Opts.Format)
|
||||||
|
|
||||||
switch c.Opts.Format {
|
switch c.Opts.Format {
|
||||||
case "jpeg", "png", "tiff", "webp", "avif":
|
case "jpeg", "png", "tiff", "webp":
|
||||||
if err := c.imageEncode(i, tmpName); err != nil {
|
if err := c.imageEncode(i, tmpName); err != nil {
|
||||||
return img, fmt.Errorf("%s: %w", fileName, err)
|
return img, fmt.Errorf("%s: %w", fileName, err)
|
||||||
}
|
}
|
||||||
case "bmp":
|
case "bmp", "avif", "jxl":
|
||||||
if err := c.imEncode(i, tmpName); err != nil {
|
if err := c.imEncode(i, tmpName); err != nil {
|
||||||
return img, fmt.Errorf("%s: %w", fileName, err)
|
return img, fmt.Errorf("%s: %w", fileName, err)
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,8 @@ func TestConvert(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := Options{}
|
opts := NewOptions()
|
||||||
opts.OutDir = tmpDir
|
opts.OutDir = tmpDir
|
||||||
opts.Archive = "zip"
|
|
||||||
opts.Quality = 75
|
|
||||||
opts.Filter = 2
|
|
||||||
|
|
||||||
conv := New(opts)
|
conv := New(opts)
|
||||||
|
|
||||||
@@ -29,7 +26,7 @@ func TestConvert(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, format := range []string{"jpeg", "png", "tiff", "bmp", "webp", "avif"} {
|
for _, format := range []string{"jpeg", "png", "tiff", "bmp", "webp", "avif", "jxl"} {
|
||||||
conv.Opts.Format = format
|
conv.Opts.Format = format
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
@@ -54,11 +51,8 @@ func TestCover(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := Options{}
|
opts := NewOptions()
|
||||||
opts.OutDir = tmpDir
|
opts.OutDir = tmpDir
|
||||||
opts.Quality = 75
|
|
||||||
opts.Filter = 2
|
|
||||||
opts.Format = "jpeg"
|
|
||||||
|
|
||||||
conv := New(opts)
|
conv := New(opts)
|
||||||
|
|
||||||
@@ -89,9 +83,8 @@ func TestThumbnail(t *testing.T) {
|
|||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := Options{}
|
opts := NewOptions()
|
||||||
opts.OutDir = tmpDir
|
opts.OutDir = tmpDir
|
||||||
opts.Filter = 2
|
|
||||||
|
|
||||||
conv := New(opts)
|
conv := New(opts)
|
||||||
|
|
||||||
|
@@ -169,10 +169,9 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
convert.IntVar(&opts.Width, "width", 0, "Image width")
|
convert.IntVar(&opts.Width, "width", 0, "Image width")
|
||||||
convert.IntVar(&opts.Height, "height", 0, "Image height")
|
convert.IntVar(&opts.Height, "height", 0, "Image height")
|
||||||
convert.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
convert.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
|
||||||
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
convert.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl")
|
||||||
convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar")
|
convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar")
|
||||||
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
|
||||||
convert.BoolVar(&opts.Lossless, "lossless", false, "Lossless compression (avif)")
|
|
||||||
convert.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
convert.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
|
||||||
convert.BoolVar(&opts.NoCover, "no-cover", false, "Do not convert the cover image")
|
convert.BoolVar(&opts.NoCover, "no-cover", false, "Do not convert the cover image")
|
||||||
convert.BoolVar(&opts.NoRGB, "no-rgb", false, "Do not convert images that have RGB colorspace")
|
convert.BoolVar(&opts.NoRGB, "no-rgb", false, "Do not convert images that have RGB colorspace")
|
||||||
@@ -255,7 +254,7 @@ func parseFlags() (cbconvert.Options, []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
convert.Usage()
|
flag.Usage()
|
||||||
_, _ = fmt.Fprintf(os.Stderr, "no command\n")
|
_, _ = fmt.Fprintf(os.Stderr, "no command\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
3
go.mod
3
go.mod
@@ -9,8 +9,7 @@ require (
|
|||||||
github.com/fvbommel/sortorder v1.1.0
|
github.com/fvbommel/sortorder v1.1.0
|
||||||
github.com/gen2brain/go-fitz v1.23.1
|
github.com/gen2brain/go-fitz v1.23.1
|
||||||
github.com/gen2brain/go-unarr v0.1.7
|
github.com/gen2brain/go-unarr v0.1.7
|
||||||
github.com/strukturag/libheif v1.15.2
|
golang.org/x/image v0.12.0
|
||||||
golang.org/x/image v0.11.0
|
|
||||||
golang.org/x/sync v0.3.0
|
golang.org/x/sync v0.3.0
|
||||||
gopkg.in/gographics/imagick.v3 v3.4.3
|
gopkg.in/gographics/imagick.v3 v3.4.3
|
||||||
)
|
)
|
||||||
|
8
go.sum
8
go.sum
@@ -10,14 +10,12 @@ github.com/gen2brain/go-fitz v1.23.1 h1:x69/szWZXpI3jZ57mMqCg7WqqvtYnQG0lXts3L6M
|
|||||||
github.com/gen2brain/go-fitz v1.23.1/go.mod h1:HU04vc+RisUh/kvEd2pB0LAxmK1oyXdN4ftyshUr9rQ=
|
github.com/gen2brain/go-fitz v1.23.1/go.mod h1:HU04vc+RisUh/kvEd2pB0LAxmK1oyXdN4ftyshUr9rQ=
|
||||||
github.com/gen2brain/go-unarr v0.1.7 h1:mEE7bPShJIsmAX67t6BW2ibpEUO7j5WK152KgNM9NbQ=
|
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/gen2brain/go-unarr v0.1.7/go.mod h1:MK9a3hddpaIxjEtrE1f/LA5yJ7gA34cS7Oyr325sY9s=
|
||||||
github.com/strukturag/libheif v1.15.2 h1:pgdcpDHqtLKRXL9ETSTeht0CsJODB3BojpTsb3S/3Wg=
|
|
||||||
github.com/strukturag/libheif v1.15.2/go.mod h1:E/PNRlmVtrtj9j2AvBZlrO4dsBDu6KfwDZn7X1Ce8Ks=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
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-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/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-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
|
golang.org/x/image v0.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||||
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
|
golang.org/x/image v0.12.0/go.mod h1:Lu90jvHG7GfemOIcldsh9A2hS01ocl6oNO7ype5mEnk=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
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/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-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -42,7 +40,7 @@ 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.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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
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.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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
Reference in New Issue
Block a user