Big update

This commit is contained in:
Milan Nikolic
2022-09-05 18:03:58 +02:00
parent 6a73f6f20e
commit cda03e0122
11 changed files with 1249 additions and 1004 deletions

281
README.md
View File

@@ -1,184 +1,171 @@
CBconvert
=========
## CBconvert
Introduction
------------
### Introduction
CBconvert is a [Comic Book](http://en.wikipedia.org/wiki/Comic_Book_Archive_file) converter written in [Go language](https://golang.org/).
CBconvert is a [Comic Book](http://en.wikipedia.org/wiki/Comic_Book_Archive_file) converter.
It can convert one comic at a time or bulk convert comics to different formats to fit your various devices.
It can convert comics to different formats to fit your various devices.
![screenshot](https://goo.gl/AxsWsA)
### Features
Features
--------
 - reads RAR, ZIP, 7Z, CBR, CBZ, CB7, CBT, PDF, EPUB, and plain directory
 - always saves processed comics in CBZ (ZIP) archive format
 - images can be converted to JPEG, PNG, TIFF, WEBP, or 4-Bit BMP (16 colors) file format
 - rotate, flip, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images
 - resize algorithms (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
 - export covers from comics
 - create thumbnails from covers by [FreeDesktop](http://www.freedesktop.org/wiki/) specification
- reads RAR, ZIP, 7Z, GZ, BZ2, CBR, CBZ, CB7, CBT, PDF, EPUB, XPS and plain directory
- always saves processed comic in CBZ (ZIP) archive format
- images can be converted to JPEG, PNG, GIF, TIFF or 4-Bit BMP (16 colors) file format
- rotate, flip, adjust brightness/contrast, adjust levels (Photoshop like) or grayscale images
- choose resize algorithm (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
- export covers from comics
- create thumbnails from covers by [freedesktop](http://www.freedesktop.org/wiki/) specification
### Download
Download
--------
 - [Windows](https://github.com/gen2brain/cbconvert/releases/download/0.7.0/cbconvert-0.7.zip)
 - [Linux](https://github.com/gen2brain/cbconvert/releases/download/0.7.0/cbconvert-0.7.tar.gz)
- [Windows GUI](https://github.com/gen2brain/cbconvert/releases/download/0.6.0/cbconvert-0.6.zip)
- [Windows CMD](https://github.com/gen2brain/cbconvert/releases/download/0.6.0/cbconvert-cmd-0.6.zip)
### Using cbconvert in file managers to generate FreeDesktop thumbnails
- [Linux 64bit GUI](https://github.com/gen2brain/cbconvert/releases/download/0.6.0/cbconvert-0.6.tar.gz)
- [Linux 64bit CMD](https://github.com/gen2brain/cbconvert/releases/download/0.6.0/cbconvert-cmd-0.6.tar.gz)
Copy cbconvert cli binary to your PATH and create file ~/.local/share/thumbnailers/cbconvert.thumbnailer :
   
    [Thumbnailer Entry]
    TryExec=cbconvert
    Exec=cbconvert thumbnail --quiet --width %s --outfile %o %i
    MimeType=application/pdf;application/x-pdf;image/pdf;application/x-cbz;application/x-cbr;application/x-cb7;application/x-cbt;application/epub+zip;
Using cbconvert in file managers to generate freedesktop thumbnails
-------------------------------------------------------------------
This is what it looks like in the PCManFM file manager:
Just copy cbconvert cmd binary to your PATH and create file ~/.local/share/thumbnailers/cbconvert.thumbnailer :
[Thumbnailer Entry]
TryExec=cbconvert
Exec=cbconvert thumbnail --quiet --width %s --outfile %o %i
MimeType=application/pdf;application/x-pdf;image/pdf;application/x-cbz;application/x-cbr;application/x-cb7;application/x-cbt;application/oxps;application/vnd.ms-xpsdocument;application/epub+zip;
This is how it looks like in PCManFM file manager:
![thumbnails](https://goo.gl/I39Otm)
![thumbnails](https://bit.ly/3BaTvTV)
Using command line app
----------------------
### Using command line app
usage: cbconvert [<flags>] <command> [<args> ...]
Comic Book convert tool.
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
--version Show application version.
--outdir="." Output directory
--size=0 Process only files larger then size (in MB)
--recursive Process subdirectories recursively
--quiet Hide console output
Args:
<args> filename or directory
Commands:
help [<command>...]
Show help.
    Usage: cbconvert <command> [<flags>] [file1 dir1 ... fileOrDirN]
convert [<flags>] <args>...
Convert archive or document (default command)
    Commands:
--width=0 Image width
--height=0 Image height
--fit Best fit for required width and height
--format="jpeg" Image format, valid values are jpeg, png, gif, tiff, bmp
--quality=75 JPEG image quality
--filter=2 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
--cover Convert cover image (use --no-cover if you want to exclude cover)
--rgb Convert images that have RGB colorspace (use --no-rgb if you only want to convert grayscaled images)
--nonimage Leave non image files in archive (use --no-nonimage to remove non image files from archive)
--grayscale Convert images to grayscale (monochromatic)
--rotate=0 Rotate images, valid values are 0, 90, 180, 270
--flip="none" Flip images, valid values are none, horizontal, vertical
--brightness=0 Adjust brightness of the images, must be in range (-100, 100)
--contrast=0 Adjust contrast of the images, must be in range (-100, 100)
--suffix=SUFFIX Add suffix to file basename
--levels-inmin=0 Shadow input value
--levels-inmax=255 Highlight input value
--levels-gamma=1 Midpoint/Gamma
--levels-outmin=0 Shadow output value
--levels-outmax=255 Highlight output value
      convert*
            Convert archive or document (default command)
cover [<flags>] <args>...
Extract cover
        --width
            Image width (default "0")
        --height
            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 (default "jpeg")
        --quality
            JPEG image quality (default "75")
        --filter
            0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
        --no-cover
            Do not convert the cover image (default "false")
        --no-rgb
            Do not convert images that have RGB colorspace (default "false")
        --no-nonimage
            Remove non-image files from the archive (default "false")
        --no-convert
       Do not transform or convert images (default "false")
        --grayscale
            Convert images to grayscale (monochromatic) (default "false")
        --rotate
            Rotate images, valid values are 0, 90, 180, 270 (default "0")
        --flip
            Flip images, valid values are none, horizontal, vertical (default "none")
        --brightness
            Adjust the brightness of the images, must be in the range (-100, 100) (default "0")
        --contrast
            Adjust the contrast of the images, must be in the range (-100, 100) (default "0")
        --suffix
            Add suffix to file basename (default "")
        --levels-inmin
            Shadow input value (default "0")
        --levels-gamma
            Midpoint/Gamma (default "1")
        --levels-inmax
            Highlight input value (default "255")
        --levels-outmin
            Shadow output value (default "0")
        --levels-outmax
            Highlight output value (default "255")
        --outdir
            Output directory (default ".")
        --size
            Process only files larger than size (in MB) (default "0")
        --recursive
            Process subdirectories recursively (default "false")
        --quiet
            Hide console output (default "false")
--width=0 Image width
--height=0 Image height
--fit Best fit for required width and height
--quality=75 JPEG image quality
--filter=2 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
      cover
            Extract cover
thumbnail [<flags>] <args>...
Extract cover thumbnail (freedesktop spec.)
        --width
            Image width (default "0")
        --height
            Image height (default "0")
        --fit
            Best fit for required width and height (default "false")
        --quality
            JPEG image quality (default "75")
        --filter
            0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
        --outdir
            Output directory (default ".")
        --size
            Process only files larger than size (in MB) (default "0")
        --recursive
            Process subdirectories recursively (default "false")
        --quiet
            Hide console output (default "false")
--width=0 Image width
--height=0 Image height
--fit Best fit for required width and height
--filter=2 0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos
      thumbnail
            Extract cover thumbnail (freedesktop spec.)
[man page](https://en.wikipedia.org/wiki/Man_page) is also available:
        --width
            Image width (default "0")
        --height
            Image height (default "0")
        --fit
            Best fit for required width and height (default "false")
        --filter
            0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos (default "2")
        --outdir
            Output directory (default ".")
        --outfile
            Output file (default "")
        --size
            Process only files larger than size (in MB) (default "0")
        --recursive
            Process subdirectories recursively (default "false")
        --quiet
            Hide console output (default "false")
cbconvert --help-man | man /dev/stdin
### Examples
Examples
--------
Rescale images to 1200px for all supported files found in a directory with a size larger than 60MB:
Rescale images to 1200px for all supported files found in directory with size larger then 60MB:
    cbconvert --recursive --width 1200 --size 60 /media/comics/Thorgal/
cbconvert --recursive --width 1200 --size 60 /media/comics/Thorgal/
Convert all images in pdf to 4bit BMP images and save the result in ~/comics directory:
Convert all images in pdf to 4bit BMP image and save result in ~/comics directory:
    cbconvert --bmp --outdir ~/comics /media/comics/Garfield/Garfield_01.pdf
cbconvert --bmp --outdir ~/comics /media/comics/Garfield/Garfield_01.pdf
[BMP](http://en.wikipedia.org/wiki/BMP_file_format) format is a very good choice for black&white pages. Archive size can be smaller 2-3x and the file will be readable by comic readers.
[BMP](http://en.wikipedia.org/wiki/BMP_file_format) format is very good choice for black&white pages. Archive size can be smaller 2-3x and file will be readable by comic readers.
Generate thumbnails by [freedesktop specification](http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html) in ~/.cache/thumbnails/normal directory with width 512:
Generate thumbnails by [freedesktop specification](http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html) in ~/.thumbnails/normal directory with width 512:
    cbconvert thumbnail --width 512 --outdir ~/.cache/thumbnails/normal /media/comics/GrooTheWanderer/
cbconvert thumbnail --width 512 --outdir ~/.thumbnails/normal /media/comics/GrooTheWanderer/
Extract covers to ~/covers dir for all supported files found in the directory, Lanczos algorithm is used for resizing:
Extract covers to ~/covers dir for all supported files found in directory, Lanczos algorithm is used for resizing:
    cbconvert cover --outdir ~/covers --filter=7 /media/comics/GrooTheWanderer/
cbconvert cover --outdir ~/covers --filter=7 /media/comics/GrooTheWanderer/
### Compile
Compile
-------
Install ImageMagick development packages, e.g. on Ubuntu:
Install imagemagick dev packages:
    apt-get install libmagickcore-dev libmagickwand-dev
apt-get install libmagickcore-dev libmagickwand-dev
Install to GOBIN:
Compile latest MuPDF:
git clone git://git.ghostscript.com/mupdf.git && cd mupdf
git submodule update --init --recursive
curl -L https://gist.githubusercontent.com/gen2brain/7869ac4c6db5933f670f/raw/1619394dc957ae10bcd73c713760993466b4bfea/mupdf-openssl-curl.patch | patch -p1
sed -e "1iHAVE_X11 = no" -e "1iWANT_OPENSSL = no" -e "1iWANT_CURL = no" -i Makerules
HAVE_X11=no HAVE_GLFW=no HAVE_GLUT=no WANT_OPENSSL=no WANT_CURL=no HAVE_MUJS=yes HAVE_JSCORE=no HAVE_V8=no make && make install
Compile unarr library:
git clone https://github.com/zeniko/unarr && cd unarr
mkdir lzma920 && cd lzma920 && curl -L http://www.7-zip.org/a/lzma920.tar.bz2 | tar -xjvp && cd ..
curl -L http://zlib.net/zlib-1.2.8.tar.gz | tar -xzvp
curl -L http://www.bzip.org/1.0.6/bzip2-1.0.6.tar.gz | tar -xzvp
curl -L https://gist.githubusercontent.com/gen2brain/89fe506863be3fb139e8/raw/8783a7d81e22ad84944d146c5e33beab6dffc641/unarr-makefile.patch | patch -p1
CFLAGS="-DHAVE_7Z -DHAVE_ZLIB -DHAVE_BZIP2 -I./lzma920/C -I./zlib-1.2.8 -I./bzip2-1.0.6" make
cp build/debug/libunarr.a /usr/lib64/ && cp unarr.h /usr/include
Install dependencies:
go get github.com/cheggaaa/pb
go get github.com/disintegration/imaging
go get github.com/gen2brain/go-fitz
go get github.com/gen2brain/go-unarr
go get github.com/gographics/imagick/imagick
go get github.com/hotei/bmp
go get github.com/skarademir/naturalsort
go get golang.org/x/image/tiff
go get golang.org/x/image/webp
go get gopkg.in/alecthomas/kingpin.v2
For command line app:
go get github.com/gen2brain/cbconvert
go build -o $GOPATH/bin/cbconvert github.com/gen2brain/cbconvert/cmd
For GUI app:
go get gopkg.in/qml.v1
go get github.com/gen2brain/cbconvert
go build -o $GOPATH/bin/cbconvert github.com/gen2brain/cbconvert/gui
    go install github.com/gen2brain/cbconvert/cmd/cbconvert@latest

File diff suppressed because it is too large Load Diff

28
cmd/cbconvert/go.mod Normal file
View File

@@ -0,0 +1,28 @@
module cbconvert
go 1.19
replace github.com/gen2brain/cbconvert => ../../
require (
github.com/gen2brain/cbconvert v0.0.0-20170124143008-5df10a58ee74
github.com/schollz/progressbar/v3 v3.10.0
github.com/spf13/pflag v1.0.5
)
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.1 // indirect
github.com/gen2brain/go-unarr v0.1.6 // 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
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
gopkg.in/gographics/imagick.v3 v3.4.1 // indirect
)

49
cmd/cbconvert/go.sum Normal file
View File

@@ -0,0 +1,49 @@
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/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/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=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.3.4 h1:3Z3Eu6FGHZWSfNKJTOUiPatWwfc7DzJRU04jFUqJODw=
github.com/rivo/uniseg v0.3.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/schollz/progressbar/v3 v3.10.0 h1:pOab0roS2jf6zkEBKAe9EyEdmRKJvhbbuxqVp9/Qjyw=
github.com/schollz/progressbar/v3 v3.10.0/go.mod h1:0N6zRwbDVLFCFy5chxuukVlRkoHWYFFLzlxQrw/sf3M=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
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/sys v0.0.0-20210615035016-665e8c7367d1/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/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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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=

222
cmd/cbconvert/main.go Normal file
View File

@@ -0,0 +1,222 @@
package main
import (
"fmt"
"os"
"os/signal"
"path/filepath"
"syscall"
"github.com/gen2brain/cbconvert"
"github.com/schollz/progressbar/v3"
flag "github.com/spf13/pflag"
)
func main() {
opts, args := parseFlags()
conv := cbconvert.New(opts)
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
for range c {
fmt.Println("\naborting")
err := os.RemoveAll(conv.Workdir)
if err != nil {
fmt.Println(err)
}
os.Exit(1)
}
}()
if _, err := os.Stat(opts.Outdir); err != nil {
err = os.MkdirAll(opts.Outdir, 0775)
if err != nil {
fmt.Println(err)
}
os.Exit(1)
}
files, err := conv.Files(args)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var bar *progressbar.ProgressBar
if opts.Cover || opts.Thumbnail {
if !opts.Quiet {
bar = progressbar.NewOptions(conv.Nfiles,
progressbar.OptionShowCount(),
progressbar.OptionClearOnFinish(),
progressbar.OptionUseANSICodes(true),
progressbar.OptionSetPredictTime(false),
)
}
}
conv.OnStart = func() {
if !opts.Quiet {
bar = progressbar.NewOptions(conv.Ncontents,
progressbar.OptionShowCount(),
progressbar.OptionClearOnFinish(),
progressbar.OptionUseANSICodes(true),
progressbar.OptionSetDescription(fmt.Sprintf("Converting %d of %d:", conv.CurrFile, conv.Nfiles)),
progressbar.OptionSetPredictTime(false),
)
}
}
conv.OnProgress = func() {
if !opts.Quiet {
_ = bar.Add(1)
}
}
conv.OnCompress = func() {
if !opts.Quiet {
_, _ = fmt.Fprintf(os.Stderr, "Compressing %d of %d...\r", conv.CurrFile, conv.Nfiles)
}
}
for _, file := range files {
stat, err := os.Stat(file)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if opts.Cover {
err = conv.ExtractCover(file, stat)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
} else if opts.Thumbnail {
err = conv.ExtractThumbnail(file, stat)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
err = conv.Convert(file, stat)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
}
}
// parseFlags parses command line flags
func parseFlags() (cbconvert.Options, []string) {
opts := cbconvert.Options{}
var args []string
convert := flag.NewFlagSet("convert", flag.ExitOnError)
convert.SortFlags = false
convert.IntVar(&opts.Width, "width", 0, "Image width")
convert.IntVar(&opts.Height, "height", 0, "Image 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")
convert.IntVar(&opts.Quality, "quality", 75, "Image quality")
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.NoRGB, "no-rgb", false, "Do not convert images that have RGB colorspace")
convert.BoolVar(&opts.NoNonImage, "no-nonimage", false, "Remove non-image files from the archive")
convert.BoolVar(&opts.NoConvert, "no-convert", false, "Do not transform or convert images")
convert.BoolVar(&opts.Grayscale, "grayscale", false, "Convert images to grayscale (monochromatic)")
convert.IntVar(&opts.Rotate, "rotate", 0, "Rotate images, valid values are 0, 90, 180, 270")
convert.StringVar(&opts.Flip, "flip", "none", "Flip images, valid values are none, horizontal, vertical")
convert.Float64Var(&opts.Brightness, "brightness", 0, "Adjust the brightness of the images, must be in the range (-100, 100)")
convert.Float64Var(&opts.Contrast, "contrast", 0, "Adjust the contrast of the images, must be in the range (-100, 100)")
convert.StringVar(&opts.Suffix, "suffix", "", "Add suffix to file basename")
convert.Float64Var(&opts.LevelsInMin, "levels-inmin", 0, "Shadow input value")
convert.Float64Var(&opts.LevelsGamma, "levels-gamma", 1.0, "Midpoint/Gamma")
convert.Float64Var(&opts.LevelsInMax, "levels-inmax", 255, "Highlight input value")
convert.Float64Var(&opts.LevelsOutMin, "levels-outmin", 0, "Shadow output value")
convert.Float64Var(&opts.LevelsOutMax, "levels-outmax", 255, "Highlight output value")
convert.StringVar(&opts.Outdir, "outdir", ".", "Output directory")
convert.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)")
convert.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively")
convert.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")
cover := flag.NewFlagSet("cover", flag.ExitOnError)
cover.SortFlags = false
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.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")
cover.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)")
cover.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively")
cover.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")
thumbnail := flag.NewFlagSet("thumbnail", flag.ExitOnError)
thumbnail.SortFlags = false
thumbnail.IntVar(&opts.Width, "width", 0, "Image width")
thumbnail.IntVar(&opts.Height, "height", 0, "Image height")
thumbnail.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and height")
thumbnail.IntVar(&opts.Filter, "filter", 2, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos")
thumbnail.StringVar(&opts.Outdir, "outdir", ".", "Output directory")
thumbnail.StringVar(&opts.Outfile, "outfile", "", "Output file")
thumbnail.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)")
thumbnail.BoolVar(&opts.Recursive, "recursive", false, "Process subdirectories recursively")
thumbnail.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")
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")
_, _ = fmt.Fprintf(os.Stderr, "\n convert*\n \tConvert archive or document (default command)\n\n")
convert.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 cover\n \tExtract cover\n\n")
cover.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 thumbnail\n \tExtract cover thumbnail (freedesktop spec.)\n\n")
thumbnail.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")
}
if len(os.Args) < 2 {
convert.Usage()
_, _ = fmt.Fprintf(os.Stderr, "no arguments\n")
os.Exit(1)
}
switch os.Args[1] {
case "convert":
_ = convert.Parse(os.Args[2:])
args = convert.Args()
case "cover":
opts.Cover = true
_ = cover.Parse(os.Args[2:])
args = cover.Args()
case "thumbnail":
opts.Thumbnail = true
_ = thumbnail.Parse(os.Args[2:])
args = thumbnail.Args()
default:
_ = convert.Parse(os.Args[1:])
args = convert.Args()
}
if len(args) == 0 {
convert.Usage()
_, _ = fmt.Fprintf(os.Stderr, "no arguments\n")
os.Exit(1)
}
return opts, args
}

37
cmd/cbconvert/make.bash Executable file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bash
MUSL="/usr/x86_64-pc-linux-musl"
MINGW="/usr/i686-w64-mingw32"
VERSION="`git --git-dir ../../.git describe --tags --abbrev=0 >/dev/null 2>&1 || 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/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MUSL/usr/lib/pkgconfig" \
CGO_CFLAGS="-I$MUSL/usr/include" \
CGO_LDFLAGS="-L$MUSL/usr/lib" \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build -tags 'extlib static' -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=i686-w64-mingw32-gcc \
PKG_CONFIG="/usr/bin/i686-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MINGW/usr/lib/pkgconfig" \
CGO_CFLAGS="-I$MINGW/usr/include" \
CGO_LDFLAGS="-L$MINGW/usr/lib" \
CGO_ENABLED=1 GOOS=windows GOARCH=386 \
go build -tags 'extlib static' -v -o ${BUILDDIR}/cbconvert.exe -ldflags "-s -w '-extldflags=-static -Wl,--allow-multiple-definition'"
cp ../../README.md ../../AUTHORS ../../COPYING ${BUILDDIR} && zip -rq "${BUILDDIR}-windows-i686.zip" ${BUILDDIR}
rm -rf ${BUILDDIR}

View File

@@ -1,149 +0,0 @@
// Author: Milan Nikolic <gen2brain@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
//go:generate goversioninfo
import (
"fmt"
"image/jpeg"
"os"
"os/signal"
"strconv"
"syscall"
"github.com/cheggaaa/pb"
"github.com/gen2brain/cbconvert"
"gopkg.in/alecthomas/kingpin.v2"
)
// Parses command line flags
func parseFlags() (cbconvert.Options, []string) {
opts := cbconvert.Options{}
var args []string
kingpin.Version("CBconvert 0.5.0")
kingpin.CommandLine.Help = "Comic Book convert tool."
kingpin.UsageTemplate(kingpin.CompactUsageTemplate)
kingpin.Flag("outdir", "Output directory").Default(".").StringVar(&opts.Outdir)
kingpin.Flag("size", "Process only files larger then size (in MB)").Default(strconv.Itoa(0)).Int64Var(&opts.Size)
kingpin.Flag("recursive", "Process subdirectories recursively").BoolVar(&opts.Recursive)
kingpin.Flag("quiet", "Hide console output").BoolVar(&opts.Quiet)
convert := kingpin.Command("convert", "Convert archive or document (default command)").Default()
convert.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&args)
convert.Flag("width", "Image width").Default(strconv.Itoa(0)).IntVar(&opts.Width)
convert.Flag("height", "Image height").Default(strconv.Itoa(0)).IntVar(&opts.Height)
convert.Flag("fit", "Best fit for required width and height").BoolVar(&opts.Fit)
convert.Flag("format", "Image format, valid values are jpeg, png, gif, tiff, bmp").Default("jpeg").StringVar(&opts.Format)
convert.Flag("quality", "JPEG image quality").Default(strconv.Itoa(jpeg.DefaultQuality)).IntVar(&opts.Quality)
convert.Flag("filter", "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos").Default(strconv.Itoa(cbconvert.Linear)).IntVar(&opts.Filter)
convert.Flag("cover", "Convert cover image (use --no-cover if you want to exclude cover)").Default("true").BoolVar(&opts.ConvertCover)
convert.Flag("rgb", "Convert images that have RGB colorspace (use --no-rgb if you only want to convert grayscaled images)").Default("true").BoolVar(&opts.RGB)
convert.Flag("nonimage", "Leave non image files in archive (use --no-nonimage to remove non image files from archive)").Default("true").BoolVar(&opts.NonImage)
convert.Flag("grayscale", "Convert images to grayscale (monochromatic)").BoolVar(&opts.Grayscale)
convert.Flag("rotate", "Rotate images, valid values are 0, 90, 180, 270").Default(strconv.Itoa(0)).IntVar(&opts.Rotate)
convert.Flag("flip", "Flip images, valid values are none, horizontal, vertical").Default("none").StringVar(&opts.Flip)
convert.Flag("brightness", "Adjust brightness of the images, must be in range (-100, 100)").Default(strconv.Itoa(0)).Float64Var(&opts.Brightness)
convert.Flag("contrast", "Adjust contrast of the images, must be in range (-100, 100)").Default(strconv.Itoa(0)).Float64Var(&opts.Contrast)
convert.Flag("suffix", "Add suffix to file basename").StringVar(&opts.Suffix)
convert.Flag("levels-inmin", "Shadow input value").Default(strconv.Itoa(0)).Float64Var(&opts.LevelsInMin)
convert.Flag("levels-gamma", "Midpoint/Gamma").Default(strconv.Itoa(1.00)).Float64Var(&opts.LevelsGamma)
convert.Flag("levels-inmax", "Highlight input value").Default(strconv.Itoa(255)).Float64Var(&opts.LevelsInMax)
convert.Flag("levels-outmin", "Shadow output value").Default(strconv.Itoa(0)).Float64Var(&opts.LevelsOutMin)
convert.Flag("levels-outmax", "Highlight output value").Default(strconv.Itoa(255)).Float64Var(&opts.LevelsOutMax)
cover := kingpin.Command("cover", "Extract cover")
cover.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&args)
cover.Flag("width", "Image width").Default(strconv.Itoa(0)).IntVar(&opts.Width)
cover.Flag("height", "Image height").Default(strconv.Itoa(0)).IntVar(&opts.Height)
cover.Flag("fit", "Best fit for required width and height").BoolVar(&opts.Fit)
cover.Flag("quality", "JPEG image quality").Default(strconv.Itoa(jpeg.DefaultQuality)).IntVar(&opts.Quality)
cover.Flag("filter", "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos").Default(strconv.Itoa(cbconvert.Linear)).IntVar(&opts.Filter)
thumbnail := kingpin.Command("thumbnail", "Extract cover thumbnail (freedesktop spec.)")
thumbnail.Arg("args", "filename or directory").Required().ExistingFilesOrDirsVar(&args)
thumbnail.Flag("outfile", "Output file").Default("").StringVar(&opts.Outfile)
thumbnail.Flag("width", "Image width").Default(strconv.Itoa(0)).IntVar(&opts.Width)
thumbnail.Flag("height", "Image height").Default(strconv.Itoa(0)).IntVar(&opts.Height)
thumbnail.Flag("fit", "Best fit for required width and height").BoolVar(&opts.Fit)
thumbnail.Flag("filter", "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 6=Gaussian, 7=Lanczos").Default(strconv.Itoa(cbconvert.Linear)).IntVar(&opts.Filter)
switch kingpin.Parse() {
case "cover":
opts.Cover = true
case "thumbnail":
opts.Thumbnail = true
}
return opts, args
}
func main() {
opts, args := parseFlags()
conv := cbconvert.NewConvertor(opts)
var bar *pb.ProgressBar
c := make(chan os.Signal, 3)
signal.Notify(c, os.Interrupt, syscall.SIGHUP, syscall.SIGTERM)
go func() {
for _ = range c {
fmt.Fprintf(os.Stderr, "Aborting\n")
os.RemoveAll(conv.Workdir)
os.Exit(1)
}
}()
if _, err := os.Stat(opts.Outdir); err != nil {
os.MkdirAll(opts.Outdir, 0777)
}
files := conv.GetFiles(args)
if opts.Cover || opts.Thumbnail {
if !opts.Quiet {
bar = pb.New(conv.Nfiles)
bar.ShowTimeLeft = false
bar.Start()
}
}
for _, file := range files {
stat, err := os.Stat(file)
if err != nil {
fmt.Fprintf(os.Stderr, "Error Stat: %v\n", err.Error())
continue
}
if opts.Cover {
conv.ExtractCover(file, stat)
if !opts.Quiet {
bar.Increment()
}
continue
} else if opts.Thumbnail {
conv.ExtractThumbnail(file, stat)
if !opts.Quiet {
bar.Increment()
}
continue
}
conv.ConvertComic(file, stat)
}
}

View File

@@ -1,28 +0,0 @@
#!/usr/bin/env bash
CHROOT="/home/milann/chroot"
MINGW="/usr/i686-w64-mingw32"
mkdir -p build
rm -f resource.syso
LIBRARY_PATH="$CHROOT/usr/lib:$CHROOT/lib" \
PKG_CONFIG_PATH="$CHROOT/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$CHROOT/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$CHROOT/usr/lib -L$CHROOT/lib" \
CC_FOR_TARGET="x86_64-pc-linux-gnu-gcc" CXX_FOR_TARGET="x86_64-pc-linux-gnu-g++" \
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -v -x -o build/cbconvert
strip build/cbconvert
go generate
PKG_CONFIG="/usr/bin/i686-w64-mingw32-pkg-config" \
PKG_CONFIG_PATH="$MINGW/usr/lib/pkgconfig" \
PKG_CONFIG_LIBDIR="$MINGW/usr/lib/pkgconfig" \
CGO_LDFLAGS="-L$MINGW/usr/lib" \
CGO_CFLAGS="-I$MINGW/usr/include -Wno-poison-system-directories" \
CGO_CXXFLAGS="-I$MINGW/usr/include -Wno-poison-system-directories" \
CGO_CPPFLAGS="-I$MINGW/usr/include -Wno-poison-system-directories" \
CC="i686-w64-mingw32-gcc" CXX="i686-w64-mingw32-g++" \
CC_FOR_TARGET="i686-w64-mingw32-gcc" CXX_FOR_TARGET="i686-w64-mingw32-g++" \
CGO_ENABLED=1 GOOS=windows GOARCH=386 go build -v -x -o build/cbconvert.exe -ldflags "-linkmode external '-extldflags=-static -Wl,--allow-multiple-definition'"
i686-w64-mingw32-strip build/cbconvert.exe

View File

@@ -1,44 +0,0 @@
{
"FixedFileInfo":
{
"FileVersion": {
"Major": 0,
"Minor": 6,
"Patch": 0,
"Build": 0
},
"ProductVersion": {
"Major": 0,
"Minor": 6,
"Patch": 0,
"Build": 0
},
"FileFlagsMask": "3f",
"FileFlags ": "00",
"FileOS": "040004",
"FileType": "01",
"FileSubType": "00"
},
"StringFileInfo":
{
"Comments": "Comic Book converter",
"CompanyName": "",
"FileDescription": "CBconvert CLI",
"FileVersion": "0.6.0",
"InternalName": "",
"LegalCopyright": "",
"LegalTrademarks": "",
"OriginalFilename": "cbconvert.exe",
"PrivateBuild": "",
"ProductName": "CBconvert",
"ProductVersion": "0.6.0",
"SpecialBuild": ""
},
"VarFileInfo":
{
"Translation": {
"LangID": "0409",
"CharsetID": "04B0"
}
}
}

15
go.mod Normal file
View File

@@ -0,0 +1,15 @@
module github.com/gen2brain/cbconvert
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/hotei/bmp v0.0.0-20150430041436-f620cebab0c7
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
)

22
go.sum Normal file
View File

@@ -0,0 +1,22 @@
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/hotei/bmp v0.0.0-20150430041436-f620cebab0c7 h1:NlUATi3cllRJhpM4mfR9BxiLRXT83bcSLcOa+S8lrME=
github.com/hotei/bmp v0.0.0-20150430041436-f620cebab0c7/go.mod h1:Hku3FQ2laCEwSv7Z8YkC0er38jLaUycUCbsFkWMr+z4=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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=