Compare commits
108 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d45a14ecc5 | |||
| 2b85ae5540 | |||
| 3fac75f088 | |||
| 0f6e32c177 | |||
| 5344970a55 | |||
| 6a54e4d5e8 | |||
| 629d569667 | |||
| 7b71fdae99 | |||
| 97554ae16c | |||
| e6e1dd33f0 | |||
| e5ea20e51f | |||
| cfaf016986 | |||
| 8155626dbb | |||
| f1c57c32dd | |||
| dd332dc282 | |||
| 18e9adabaa | |||
| 4e2f78026d | |||
| e1134cd902 | |||
| f289c9cd06 | |||
| 0439a2edde | |||
| 92c49290ae | |||
| 7f025aa9c2 | |||
| a8b2f65ca5 | |||
| 8f75ffc43b | |||
| aca062a3f2 | |||
| 9de98599a9 | |||
| f65fc4bafa | |||
| 26a320ef9d | |||
| c4f236bdf9 | |||
| 23ac71ee0c | |||
| 3f0ae41456 | |||
| 04a047aa2e | |||
| 1284b9ded7 | |||
| b8d82e920c | |||
| 3c81595e08 | |||
| 0f6fbba1d7 | |||
| 47a10e9185 | |||
| a5817c3ba5 | |||
| b7c422fe33 | |||
| 09630243fb | |||
| 59bc2b7a1e | |||
| 3a627d92e1 | |||
| f555f09e59 | |||
| ad4f522b1f | |||
| 05f08f46ec | |||
| 4d845afa43 | |||
| 0dee611bf1 | |||
| 4bd35fe570 | |||
| f85fe990ff | |||
| 31e1c94f70 | |||
| 8e14a22a9b | |||
| 5d20f5c81e | |||
| a2738b4c08 | |||
| a0b1f65940 | |||
| 1e0f5b5956 | |||
| 544d824afb | |||
| 90ed56d4ad | |||
| eff4730014 | |||
| 85059d7df3 | |||
| 9e944142d6 | |||
| ee2cf2e14d | |||
| bf95d62242 | |||
| be50bf1e16 | |||
| 1a5520c050 | |||
| 4f23f228a8 | |||
| 5b2ed5233b | |||
| e2c41b3193 | |||
| 4c51c152f3 | |||
| c5cbc9fa9b | |||
| a668ae5e3d | |||
| 3bd7e672eb | |||
| 6b21c36902 | |||
| d54cec94a2 | |||
| 0df2f7d39d | |||
| e7b9fd8c2c | |||
| f795c70c33 | |||
| 3bb40a87cd | |||
| e38b5b3fb4 | |||
| f57b2598f1 | |||
| fca9f31fcf | |||
| 5f5fa2bb80 | |||
| 622c82a9df | |||
| f4c149ac31 | |||
| de2f8a2035 | |||
| 749ff0b943 | |||
| 1f33bec28c | |||
| 6a4054455a | |||
| 962a2e6dee | |||
| 63013998c7 | |||
| c0a077fcd1 | |||
| 71d8f9dbb6 | |||
| c794ea6b9d | |||
| 74c5de6994 | |||
| 1a36ec17b2 | |||
| a9e3281a5f | |||
| 962fde8c8d | |||
| a8084ed897 | |||
| 183c42613f | |||
| cad75231b1 | |||
| c32019615d | |||
| dd9000cfea | |||
| 4fc3209b4e | |||
| 000bd20f71 | |||
| f558b109e5 | |||
| 5922f102be | |||
| f84d2d4333 | |||
| 551ab9e137 | |||
| 046cc2c1fd |
@@ -0,0 +1,43 @@
|
||||
on: [push, pull_request]
|
||||
name: Build
|
||||
jobs:
|
||||
cli:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
working-directory: cmd/cbconvert
|
||||
env:
|
||||
CGO_ENABLED: 1
|
||||
|
||||
gui:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
- name: Install dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
working-directory: cmd/cbconvert-gui
|
||||
env:
|
||||
CGO_ENABLED: 1
|
||||
@@ -1,120 +1,20 @@
|
||||
on: [push, pull_request]
|
||||
name: Test
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
test:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
- name: Setup cmake
|
||||
uses: jwlawson/actions-setup-cmake@v1.13
|
||||
with:
|
||||
cmake-version: '3.27.x'
|
||||
|
||||
- name: Update apt-get
|
||||
run: |
|
||||
sudo apt-get update
|
||||
|
||||
- name: Wget libheif
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: https://github.com/strukturag/libheif/releases/download/v1.15.2/libheif-1.15.2.tar.gz
|
||||
- name: Unpack libheif
|
||||
run: |
|
||||
tar -xpf libheif-1.15.2.tar.gz
|
||||
- name: Install libheif dependencies
|
||||
run: |
|
||||
sudo apt-get install libaom-dev -y
|
||||
- name: Configure libheif
|
||||
working-directory: libheif-1.15.2
|
||||
run: |
|
||||
./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-examples
|
||||
- name: Install libheif
|
||||
working-directory: libheif-1.15.2
|
||||
run: |
|
||||
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
|
||||
uses: wei/wget@v1
|
||||
with:
|
||||
args: -O ImageMagick-7.1.1-15.tar.gz https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.1-15.tar.gz
|
||||
- name: Unpack ImageMagick
|
||||
run: |
|
||||
tar -xpf ImageMagick-7.1.1-15.tar.gz
|
||||
- name: Configure ImageMagick
|
||||
working-directory: ImageMagick-7.1.1-15
|
||||
run: |
|
||||
./configure --prefix=/usr --libdir=/usr/lib/x86_64-linux-gnu --enable-shared --disable-static --enable-zero-configuration \
|
||||
--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-fpx --without-fontconfig --without-freetype --without-gslib --without-gvc --without-jbig --without-openjp2 \
|
||||
--without-lcms --without-lqr --without-lzma --without-openexr --without-pango --without-raw --without-rsvg --without-wmf \
|
||||
--without-xml --disable-openmp --with-jpeg --with-heic --with-jxl --with-png --with-tiff --with-webp
|
||||
- name: Install ImageMagick
|
||||
working-directory: ImageMagick-7.1.1-15
|
||||
run: |
|
||||
make -j3 && sudo make install
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.21
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
- name: Test
|
||||
run: go test
|
||||
run: go test -v ./...
|
||||
env:
|
||||
CGO_ENABLED: 1
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
cmd/cbconvert-gui/cbconvert-gui
|
||||
cmd/cbconvert/cbconvert
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
@@ -8,163 +8,197 @@ It can convert comics to different formats to fit your various devices.
|
||||
|
||||
<img src="cmd/cbconvert-gui/screenshots/linux-01.jpg" width="700" alt="screenshot" />
|
||||
|
||||
See more [screnshots](https://github.com/gen2brain/cbconvert/blob/master/cmd/cbconvert-gui/screenshots/).
|
||||
See more [screenshots](https://github.com/gen2brain/cbconvert/blob/master/cmd/cbconvert-gui/screenshots/).
|
||||
|
||||
### Features
|
||||
|
||||
* 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, DOCX, PPTX and plain directory
|
||||
* saves processed files in ZIP archive format or TAR
|
||||
* images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, JXL, or 4-Bit BMP (16 colors) image format
|
||||
* rotate, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images
|
||||
* rotate, adjust brightness/contrast or grayscale images
|
||||
* resize filters (NearestNeighbor, Box, Linear, MitchellNetravali, CatmullRom, Gaussian, Lanczos)
|
||||
* export covers from comics
|
||||
* create thumbnails from covers by [FreeDesktop](http://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html) specification
|
||||
|
||||
### Download
|
||||
|
||||
* [Windows x86_64](https://github.com/gen2brain/cbconvert/releases/latest/download/cbconvert-1.0.0-windows-x86_64.zip)
|
||||
* [Linux x86_64](https://github.com/gen2brain/cbconvert/releases/latest/download/cbconvert-1.0.0-linux-x86_64.tar.gz)
|
||||
* [macOS x86_64](https://github.com/gen2brain/cbconvert/releases/latest/download/cbconvert-1.0.0-darwin-x86_64.zip)
|
||||
* [macOS aarch64](https://github.com/gen2brain/cbconvert/releases/latest/download/cbconvert-1.0.0-darwin-aarch64.zip)
|
||||
Download the latest binaries from the [releases](https://github.com/gen2brain/cbconvert/releases).
|
||||
|
||||
Linux Flatpak is available at [Flathub](https://flathub.org/apps/io.github.gen2brain.cbconvert).
|
||||
|
||||
### Compile
|
||||
|
||||
You must have `CGO_ENABLED=1`. Note that `Go` will disable cgo when cross-compiling.
|
||||
|
||||
Install to `GOBIN` (you can point `GOBIN` to e.g. `/usr/local/bin` or `~/.local/bin`):
|
||||
|
||||
`go install github.com/gen2brain/cbconvert/cmd/cbconvert@latest`
|
||||
|
||||
For GUI app, check [IUP](https://github.com/gen2brain/iup-go) requirements, and then install:
|
||||
|
||||
`go install github.com/gen2brain/cbconvert/cmd/cbconvert-gui@latest`
|
||||
|
||||
### Build tags
|
||||
|
||||
* `extlib` - use external `libmupdf` library
|
||||
* `pkgconfig` - enable pkg-config (used with `extlib`)
|
||||
|
||||
### Using cbconvert in file managers to generate FreeDesktop thumbnails
|
||||
|
||||
Copy `cbconvert` cli binary to your PATH and create file `~/.local/share/thumbnailers/cbconvert.thumbnailer`:
|
||||
Copy/install `cbconvert` cli binary to your `PATH`, create file `~/.local/share/thumbnailers/cbconvert.thumbnailer`
|
||||
and paste contents from [thumbnailer](https://github.com/gen2brain/cbconvert/tree/master/cmd/cbconvert-gui/dist/linux/io.github.gen2brain.cbconvert.thumbnailer).
|
||||
|
||||
```
|
||||
[Thumbnailer Entry]
|
||||
TryExec=cbconvert
|
||||
Exec=cbconvert thumbnail --quiet --width %s --outfile %o %i
|
||||
MimeType=application/pdf;application/x-cb7;application/x-cbt;application/epub+zip;application/vnd.comicbook-rar;application/vnd.comicbook+zip;application/x-mobipocket-ebook;application/vnd.ms-xpsdocument;
|
||||
```
|
||||
This is what it looks like in the `PCManFM` file manager:
|
||||
|
||||
This is what it looks like in the PCManFM file manager:
|
||||
|
||||
<img src="cmd/cbconvert/screenshots/thumbnails.jpg" width="700" alt="thumbnails" />
|
||||
<img src="cmd/cbconvert-gui/screenshots/thumbnails.jpg" width="700" alt="thumbnails" />
|
||||
|
||||
|
||||
### Using command line app
|
||||
|
||||
```
|
||||
Usage: cbconvert <command> [<flags>] [file1 dir1 ... fileOrDirN]
|
||||
Usage: cbconvert <command> [<flags>] [file1 dir1 ... fileOrDirN]
|
||||
|
||||
|
||||
Commands:
|
||||
Commands:
|
||||
|
||||
convert
|
||||
Convert archive or document
|
||||
convert
|
||||
Convert archive or document
|
||||
|
||||
--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, avif, jxl (default "jpeg")
|
||||
--archive
|
||||
Archive format, valid values are zip, tar (default "zip")
|
||||
--quality
|
||||
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")
|
||||
--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")
|
||||
--profile
|
||||
Load a saved GUI profile as defaults; explicit flags still override (default "")
|
||||
--width
|
||||
Image width (default "0")
|
||||
--height
|
||||
Image height (default "0")
|
||||
--fit
|
||||
Best fit for required width and height (default "false")
|
||||
--no-upscale
|
||||
Do not upscale images already smaller than the requested width/height (default "false")
|
||||
--dpi
|
||||
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
||||
--format
|
||||
Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl (default "jpeg")
|
||||
--archive
|
||||
Archive format, valid values are zip, tar (default "zip")
|
||||
--zip-level
|
||||
ZIP compression level, 0 disables compression, 1-9 sets deflate level (1 fastest, 9 smallest), -1 uses the default (default "-1")
|
||||
--quality
|
||||
Image quality (default "75")
|
||||
--effort
|
||||
Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default (default "-1")
|
||||
--lossless
|
||||
Lossless compression (webp, avif, jxl), ignores quality (default "false")
|
||||
--combine
|
||||
Combine all inputs into a single archive (default "false")
|
||||
--outfile
|
||||
Output file name for --combine (default: first input + -combined) (default "")
|
||||
--filter
|
||||
0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 5=Gaussian, 6=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")
|
||||
--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 "")
|
||||
--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")
|
||||
|
||||
cover
|
||||
Extract cover
|
||||
cover
|
||||
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, avif, jxl (default "jpeg")
|
||||
--quality
|
||||
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")
|
||||
--profile
|
||||
Load a saved GUI profile as defaults; explicit flags still override (default "")
|
||||
--width
|
||||
Image width (default "0")
|
||||
--height
|
||||
Image height (default "0")
|
||||
--fit
|
||||
Best fit for required width and height (default "false")
|
||||
--no-upscale
|
||||
Do not upscale images already smaller than the requested width/height (default "false")
|
||||
--dpi
|
||||
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
||||
--format
|
||||
Image format, valid values are jpeg, png, tiff, bmp, webp, avif (default "jpeg")
|
||||
--quality
|
||||
Image quality (default "75")
|
||||
--effort
|
||||
Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default (default "-1")
|
||||
--lossless
|
||||
Lossless compression (webp, avif, jxl), ignores quality (default "false")
|
||||
--filter
|
||||
0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 5=Gaussian, 6=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")
|
||||
|
||||
thumbnail
|
||||
Extract cover thumbnail (freedesktop spec.)
|
||||
thumbnail
|
||||
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")
|
||||
--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")
|
||||
--profile
|
||||
Load a saved GUI profile as defaults; explicit flags still override (default "")
|
||||
--width
|
||||
Image width (default "0")
|
||||
--height
|
||||
Image height (default "0")
|
||||
--fit
|
||||
Best fit for required width and height (default "false")
|
||||
--no-upscale
|
||||
Do not upscale images already smaller than the requested width/height (default "false")
|
||||
--dpi
|
||||
Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the default (300) (default "0")
|
||||
--filter
|
||||
0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 5=Gaussian, 6=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")
|
||||
|
||||
meta
|
||||
CBZ metadata
|
||||
meta
|
||||
CBZ metadata
|
||||
|
||||
--cover
|
||||
Print cover name (default "false")
|
||||
--comment
|
||||
Print zip comment (default "false")
|
||||
--comment-body
|
||||
Set zip comment (default "")
|
||||
--file-add
|
||||
Add file to archive (default "")
|
||||
--file-remove
|
||||
Remove file(s) from archive (glob pattern, i.e. *.xml) (default "")
|
||||
--cover
|
||||
Print cover name (default "false")
|
||||
--comment
|
||||
Print zip comment (default "false")
|
||||
--comment-body
|
||||
Set zip comment (default "")
|
||||
--file-add
|
||||
Add file to archive (default "")
|
||||
--file-remove
|
||||
Remove file from archive (glob pattern, i.e. *.xml) (default "")
|
||||
|
||||
version
|
||||
Print version
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -187,6 +221,10 @@ This is what it looks like in the PCManFM file manager:
|
||||
|
||||
`cbconvert --format avif --quality 50 --width 1280 --outdir ~/comics /media/comics/Misc/`
|
||||
|
||||
* Combine several issues into a single archive:
|
||||
|
||||
`cbconvert convert --combine --outfile Series.cbz --outdir ~/comics issue1.cbz issue2.cbr issue3.cb7`
|
||||
|
||||
### Quality settings
|
||||
|
||||
This table maps quality settings for JPEG to the respective AVIF and WEBP quality settings:
|
||||
@@ -197,12 +235,3 @@ This table maps quality settings for JPEG to the respective AVIF and WEBP qualit
|
||||
| AVIF quality | 48 | 51 | 56 | 64 |
|
||||
| WEBP quality | 55 | 64 | 72 | 82 |
|
||||
|
||||
### Compile
|
||||
|
||||
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`
|
||||
|
||||
For GUI app, check [IUP requirements](https://github.com/gen2brain/iup-go), and then install:
|
||||
|
||||
`go install -tags extlib github.com/gen2brain/cbconvert/cmd/cbconvert-gui@latest`
|
||||
|
||||
@@ -3,16 +3,20 @@ package cbconvert
|
||||
import (
|
||||
"archive/tar"
|
||||
"archive/zip"
|
||||
"compress/flate"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/go-unarr"
|
||||
"github.com/mholt/archives"
|
||||
)
|
||||
|
||||
// archiveSave saves workdir to CBZ archive.
|
||||
func (c *Convertor) archiveSave(fileName string) error {
|
||||
func (c *Converter) archiveSave(fileName string) error {
|
||||
if c.Opts.Archive == "zip" {
|
||||
return c.archiveSaveZip(fileName)
|
||||
} else if c.Opts.Archive == "tar" {
|
||||
@@ -23,19 +27,19 @@ func (c *Convertor) archiveSave(fileName string) error {
|
||||
}
|
||||
|
||||
// archiveSaveZip saves workdir to CBZ archive.
|
||||
func (c *Convertor) archiveSaveZip(fileName string) error {
|
||||
func (c *Converter) archiveSaveZip(fileName string) error {
|
||||
if c.OnCompress != nil {
|
||||
c.OnCompress()
|
||||
}
|
||||
|
||||
var zipName string
|
||||
if c.Opts.Recursive {
|
||||
err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755)
|
||||
if err != nil {
|
||||
outDir := c.recursiveDir(fileName)
|
||||
if err := os.MkdirAll(outDir, 0755); err != nil {
|
||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
||||
}
|
||||
|
||||
zipName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbz", baseNoExt(fileName), c.Opts.Suffix))
|
||||
zipName = filepath.Join(outDir, fmt.Sprintf("%s%s.cbz", baseNoExt(fileName), c.Opts.Suffix))
|
||||
} else {
|
||||
zipName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbz", baseNoExt(fileName), c.Opts.Suffix))
|
||||
}
|
||||
@@ -47,6 +51,13 @@ func (c *Convertor) archiveSaveZip(fileName string) error {
|
||||
|
||||
z := zip.NewWriter(zipFile)
|
||||
|
||||
if c.Opts.ZipLevel >= 1 {
|
||||
level := c.Opts.ZipLevel
|
||||
z.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
|
||||
return flate.NewWriter(out, level)
|
||||
})
|
||||
}
|
||||
|
||||
files, err := os.ReadDir(c.Workdir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
||||
@@ -69,6 +80,9 @@ func (c *Convertor) archiveSaveZip(fileName string) error {
|
||||
}
|
||||
|
||||
zipInfo.Method = zip.Deflate
|
||||
if c.Opts.ZipLevel == 0 {
|
||||
zipInfo.Method = zip.Store
|
||||
}
|
||||
w, err := z.CreateHeader(zipInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveSaveZip: %w", err)
|
||||
@@ -97,19 +111,19 @@ func (c *Convertor) archiveSaveZip(fileName string) error {
|
||||
}
|
||||
|
||||
// archiveSaveTar saves workdir to CBT archive.
|
||||
func (c *Convertor) archiveSaveTar(fileName string) error {
|
||||
func (c *Converter) archiveSaveTar(fileName string) error {
|
||||
if c.OnCompress != nil {
|
||||
c.OnCompress()
|
||||
}
|
||||
|
||||
var tarName string
|
||||
if c.Opts.Recursive {
|
||||
err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755)
|
||||
if err != nil {
|
||||
outDir := c.recursiveDir(fileName)
|
||||
if err := os.MkdirAll(outDir, 0755); err != nil {
|
||||
return fmt.Errorf("archiveSaveTar: %w", err)
|
||||
}
|
||||
|
||||
tarName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbt", baseNoExt(fileName), c.Opts.Suffix))
|
||||
tarName = filepath.Join(outDir, fmt.Sprintf("%s%s.cbt", baseNoExt(fileName), c.Opts.Suffix))
|
||||
} else {
|
||||
tarName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbt", baseNoExt(fileName), c.Opts.Suffix))
|
||||
}
|
||||
@@ -169,17 +183,70 @@ func (c *Convertor) archiveSaveTar(fileName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// archiveOpen identifies the archive and returns its extractor and a reader positioned at the start.
|
||||
func archiveOpen(ctx context.Context, fileName string) (io.ReadCloser, archives.Extractor, io.Reader, error) {
|
||||
file, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
format, input, err := archives.Identify(ctx, fileName, file)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
ex, ok := format.(archives.Extractor)
|
||||
if !ok {
|
||||
file.Close()
|
||||
return nil, nil, nil, fmt.Errorf("%s: unsupported archive format", fileName)
|
||||
}
|
||||
|
||||
return file, ex, input, nil
|
||||
}
|
||||
|
||||
// FileType returns the detected archive container, document extension, or "DIR".
|
||||
func FileType(path string) string {
|
||||
if isArchive(path) {
|
||||
file, err := os.Open(path)
|
||||
if err == nil {
|
||||
defer file.Close()
|
||||
|
||||
format, _, err := archives.Identify(context.Background(), path, file)
|
||||
if err == nil {
|
||||
return strings.ToUpper(strings.TrimPrefix(format.Extension(), "."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info, err := os.Stat(path); err == nil && info.IsDir() {
|
||||
return "DIR"
|
||||
}
|
||||
|
||||
return strings.ToUpper(strings.TrimPrefix(filepath.Ext(path), "."))
|
||||
}
|
||||
|
||||
// archiveList lists contents of archive.
|
||||
func (c *Convertor) archiveList(fileName string) ([]string, error) {
|
||||
func (c *Converter) archiveList(fileName string) ([]string, error) {
|
||||
var contents []string
|
||||
|
||||
archive, err := unarr.NewArchive(fileName)
|
||||
ctx := context.Background()
|
||||
|
||||
file, ex, input, err := archiveOpen(ctx, fileName)
|
||||
if err != nil {
|
||||
return contents, fmt.Errorf("archiveList: %w", err)
|
||||
}
|
||||
defer archive.Close()
|
||||
defer file.Close()
|
||||
|
||||
contents, err = archive.List()
|
||||
err = ex.Extract(ctx, input, func(ctx context.Context, f archives.FileInfo) error {
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
contents = append(contents, f.NameInArchive)
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return contents, fmt.Errorf("archiveList: %w", err)
|
||||
}
|
||||
@@ -187,8 +254,45 @@ func (c *Convertor) archiveList(fileName string) ([]string, error) {
|
||||
return contents, nil
|
||||
}
|
||||
|
||||
// archiveFile returns the contents of a single named file from the archive.
|
||||
func (c *Converter) archiveFile(fileName, name string) ([]byte, error) {
|
||||
var data []byte
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
file, ex, input, err := archiveOpen(ctx, fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archiveFile: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = ex.Extract(ctx, input, func(ctx context.Context, f archives.FileInfo) error {
|
||||
if f.NameInArchive != name {
|
||||
return nil
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
data, err = io.ReadAll(rc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fs.SkipAll
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("archiveFile: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// archiveComment returns ZIP comment.
|
||||
func (c *Convertor) archiveComment(fileName string) (string, error) {
|
||||
func (c *Converter) archiveComment(fileName string) (string, error) {
|
||||
zr, err := zip.OpenReader(fileName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("archiveComment: %w", err)
|
||||
@@ -199,7 +303,7 @@ func (c *Convertor) archiveComment(fileName string) (string, error) {
|
||||
}
|
||||
|
||||
// archiveSetComment sets ZIP comment.
|
||||
func (c *Convertor) archiveSetComment(fileName, commentBody string) error {
|
||||
func (c *Converter) archiveSetComment(fileName, commentBody string) error {
|
||||
zr, err := zip.OpenReader(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveSetComment: %w", err)
|
||||
@@ -262,8 +366,8 @@ func (c *Convertor) archiveSetComment(fileName, commentBody string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// archiveFileAdd adds file to archive.
|
||||
func (c *Convertor) archiveFileAdd(fileName, newFileName string) error {
|
||||
// archiveFileAdd adds a file to the archive.
|
||||
func (c *Converter) archiveFileAdd(fileName, newFileName string) error {
|
||||
zr, err := zip.OpenReader(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveFileAdd: %w", err)
|
||||
@@ -353,7 +457,7 @@ func (c *Convertor) archiveFileAdd(fileName, newFileName string) error {
|
||||
}
|
||||
|
||||
// archiveFileRemove removes files from archive.
|
||||
func (c *Convertor) archiveFileRemove(fileName, pattern string) error {
|
||||
func (c *Converter) archiveFileRemove(fileName, pattern string) error {
|
||||
zr, err := zip.OpenReader(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("archiveFileRemove: %w", err)
|
||||
@@ -0,0 +1,461 @@
|
||||
package cbconvert
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gen2brain/avif"
|
||||
"github.com/gen2brain/go-fitz"
|
||||
"github.com/gen2brain/jpegli"
|
||||
"github.com/gen2brain/jpegn"
|
||||
"github.com/gen2brain/jpegxl"
|
||||
"github.com/gen2brain/webp"
|
||||
"github.com/jsummers/gobmp"
|
||||
"github.com/mholt/archives"
|
||||
"golang.org/x/image/tiff"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// convertDocument converts PDF/EPUB document to CBZ.
|
||||
func (c *Converter) convertDocument(ctx context.Context, fileName string) error {
|
||||
doc, err := fitz.New(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDocument: %w", err)
|
||||
}
|
||||
|
||||
defer doc.Close()
|
||||
|
||||
c.Ncontents = doc.NumPage()
|
||||
c.CurrContent = 0
|
||||
|
||||
if c.OnStart != nil {
|
||||
c.OnStart()
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.SetLimit(runtime.NumCPU() + 1)
|
||||
|
||||
for n := 0; n < c.Ncontents; n++ {
|
||||
if ctx.Err() != nil {
|
||||
return fmt.Errorf("convertDocument: %w", ctx.Err())
|
||||
}
|
||||
|
||||
img, err := c.renderPage(doc, n)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDocument: %w", err)
|
||||
}
|
||||
|
||||
if img != nil {
|
||||
eg.Go(func() error {
|
||||
return c.imageConvert(ctx, img, n, "")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDocument: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertArchive converts archive to CBZ.
|
||||
func (c *Converter) convertArchive(ctx context.Context, fileName string) error {
|
||||
contents, err := c.archiveList(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
images := imagesFromSlice(contents)
|
||||
|
||||
c.Ncontents = len(images)
|
||||
c.CurrContent = 0
|
||||
|
||||
if c.OnStart != nil {
|
||||
c.OnStart()
|
||||
}
|
||||
|
||||
cover := c.coverName(images)
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.SetLimit(runtime.NumCPU() + 1)
|
||||
|
||||
file, ex, input, err := archiveOpen(ctx, fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = ex.Extract(ctx, input, func(ctx context.Context, f archives.FileInfo) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
pathName := f.NameInArchive
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(rc)
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
if err = rc.Close(); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
if isImage(pathName) {
|
||||
if c.Opts.NoConvert {
|
||||
if err = copyFile(bytes.NewReader(data), c.workPath(flatName(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if cover == pathName && c.Opts.NoCover {
|
||||
if err = copyFile(bytes.NewReader(data), c.workPath(flatName(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var img image.Image
|
||||
img, err = c.imageDecode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
if c.Opts.NoRGB && !isGrayScale(img) {
|
||||
if err = copyFile(bytes.NewReader(data), c.workPath(flatName(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if img != nil {
|
||||
eg.Go(func() error {
|
||||
return c.imageConvert(ctx, img, 0, pathName)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if filepath.Ext(pathName) == ".DS_Store" || strings.Contains(pathName, "__MACOSX") {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.prefix == "" && !c.Opts.NoNonImage {
|
||||
if err = copyFile(bytes.NewReader(data), c.workPath(flatName(pathName))); err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if werr := eg.Wait(); werr != nil {
|
||||
return fmt.Errorf("convertArchive: %w", werr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertArchive: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertDirectory converts directory to CBZ.
|
||||
func (c *Converter) convertDirectory(ctx context.Context, dirPath string) error {
|
||||
contents, err := imagesFromPath(dirPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
images := imagesFromSlice(contents)
|
||||
c.Ncontents = len(images)
|
||||
c.CurrContent = 0
|
||||
|
||||
if c.OnStart != nil {
|
||||
c.OnStart()
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
eg.SetLimit(runtime.NumCPU() + 1)
|
||||
|
||||
for index, img := range contents {
|
||||
if ctx.Err() != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", ctx.Err())
|
||||
}
|
||||
|
||||
rel, rerr := filepath.Rel(dirPath, img)
|
||||
if rerr != nil {
|
||||
rel = filepath.Base(img)
|
||||
}
|
||||
|
||||
file, err := os.Open(img)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if isNonImage(img) && c.prefix == "" && !c.Opts.NoNonImage {
|
||||
if err = copyFile(file, c.workPath(flatName(rel))); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
continue
|
||||
} else if isImage(img) {
|
||||
if c.Opts.NoConvert {
|
||||
if err = copyFile(file, c.workPath(flatName(rel))); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
var i image.Image
|
||||
i, err = c.imageDecode(file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if c.Opts.NoRGB && !isGrayScale(i) {
|
||||
if err = copyFile(file, c.workPath(flatName(rel))); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
if i != nil {
|
||||
eg.Go(func() error {
|
||||
return c.imageConvert(ctx, i, index, rel)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = eg.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("convertDirectory: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// workPath returns the path of name inside the workdir, with the combine prefix applied.
|
||||
func (c *Converter) workPath(name string) string {
|
||||
return filepath.Join(c.Workdir, c.prefix+name)
|
||||
}
|
||||
|
||||
// imageConvert converts image.Image.
|
||||
func (c *Converter) imageConvert(ctx context.Context, img image.Image, index int, pathName string) error {
|
||||
err := ctx.Err()
|
||||
if err != nil {
|
||||
return fmt.Errorf("imageConvert: %w", err)
|
||||
}
|
||||
|
||||
atomic.AddInt32(&c.CurrContent, 1)
|
||||
if c.OnProgress != nil {
|
||||
c.OnProgress()
|
||||
}
|
||||
|
||||
ext := c.Opts.Format
|
||||
if ext == "jpeg" {
|
||||
ext = "jpg"
|
||||
}
|
||||
|
||||
var fileName string
|
||||
if pathName != "" {
|
||||
fileName = c.workPath(fmt.Sprintf("%s.%s", flatName(strings.TrimSuffix(pathName, filepath.Ext(pathName))), ext))
|
||||
} else {
|
||||
fileName = c.workPath(fmt.Sprintf("%03d.%s", index, ext))
|
||||
}
|
||||
|
||||
img = c.imageTransform(img)
|
||||
|
||||
w, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("imageConvert: %w", err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
if err := c.imageEncode(img, w); err != nil {
|
||||
return fmt.Errorf("imageConvert: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// imageTransform transforms image (resize, rotate, brightness, contrast).
|
||||
func (c *Converter) imageTransform(img image.Image) image.Image {
|
||||
var i = img
|
||||
|
||||
if c.Opts.Width > 0 || c.Opts.Height > 0 {
|
||||
i = c.resizeFit(i)
|
||||
}
|
||||
|
||||
if c.Opts.Rotate > 0 {
|
||||
switch c.Opts.Rotate {
|
||||
case 90:
|
||||
i = rotate(i, 90)
|
||||
case 180:
|
||||
i = rotate(i, 180)
|
||||
case 270:
|
||||
i = rotate(i, 270)
|
||||
}
|
||||
}
|
||||
|
||||
if c.Opts.Brightness != 0 {
|
||||
i = brightness(i, float64(c.Opts.Brightness))
|
||||
}
|
||||
|
||||
if c.Opts.Contrast != 0 {
|
||||
i = contrast(i, float64(c.Opts.Contrast))
|
||||
}
|
||||
|
||||
if c.Opts.Grayscale {
|
||||
i = imageToGray(i)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// imageDecode decodes image from reader.
|
||||
// jpegnOptions decodes straight to RGBA with high-quality chroma upsampling.
|
||||
var jpegnOptions = jpegn.Options{ToRGBA: true, UpsampleMethod: jpegn.CatmullRom}
|
||||
|
||||
func (c *Converter) imageDecode(reader io.Reader) (image.Image, error) {
|
||||
br := bufio.NewReader(reader)
|
||||
|
||||
if magic, err := br.Peek(2); err == nil && magic[0] == 0xff && magic[1] == 0xd8 {
|
||||
opts := jpegnOptions
|
||||
|
||||
if c.previewWidth > 0 && c.previewHeight > 0 {
|
||||
data, err := io.ReadAll(br)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imageDecode: %w", err)
|
||||
}
|
||||
|
||||
if cfg, err := jpegn.DecodeConfig(bytes.NewReader(data)); err == nil {
|
||||
opts.ScaleDenom = scaleDenom(cfg.Width, cfg.Height, c.previewWidth, c.previewHeight)
|
||||
}
|
||||
|
||||
img, err := jpegn.Decode(bytes.NewReader(data), &opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imageDecode: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
img, err := jpegn.Decode(br, &opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("imageDecode: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
img, _, err := image.Decode(br)
|
||||
if err != nil {
|
||||
return img, fmt.Errorf("imageDecode: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// scaleDenom returns the largest JPEG IDCT denominator (1, 2, 4, 8) that keeps w x h at or above tw x th.
|
||||
func scaleDenom(w, h, tw, th int) int {
|
||||
for _, d := range []int{8, 4, 2} {
|
||||
if w/d >= tw && h/d >= th {
|
||||
return d
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// imageEncode encodes image to file.
|
||||
func (c *Converter) imageEncode(img image.Image, w io.Writer) error {
|
||||
var err error
|
||||
|
||||
switch c.Opts.Format {
|
||||
case "png":
|
||||
err = png.Encode(w, img)
|
||||
case "tiff":
|
||||
err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Uncompressed})
|
||||
case "jpeg":
|
||||
opts := &jpegli.EncodingOptions{}
|
||||
opts.Quality = c.Opts.Quality
|
||||
opts.ChromaSubsampling = image.YCbCrSubsampleRatio420
|
||||
opts.ProgressiveLevel = 2
|
||||
opts.AdaptiveQuantization = true
|
||||
opts.DCTMethod = jpegli.DefaultDCTMethod
|
||||
err = jpegli.Encode(w, img, opts)
|
||||
case "webp":
|
||||
method := webp.DefaultMethod
|
||||
if c.Opts.Effort >= 0 {
|
||||
method = min(max(c.Opts.Effort, 0), 6)
|
||||
}
|
||||
err = webp.Encode(w, img, webp.Options{Quality: c.Opts.Quality, Method: method, Lossless: c.Opts.Lossless})
|
||||
case "avif":
|
||||
speed := avif.DefaultSpeed
|
||||
if c.Opts.Effort >= 0 {
|
||||
speed = min(max(c.Opts.Effort, 0), 10)
|
||||
}
|
||||
err = avif.Encode(w, img, avif.Options{Quality: c.Opts.Quality, Speed: speed, Lossless: c.Opts.Lossless})
|
||||
case "jxl":
|
||||
effort := jpegxl.DefaultEffort
|
||||
if c.Opts.Effort >= 0 {
|
||||
effort = min(max(c.Opts.Effort, 1), 10)
|
||||
}
|
||||
err = jpegxl.Encode(w, img, jpegxl.Options{Quality: c.Opts.Quality, Effort: effort, Lossless: c.Opts.Lossless})
|
||||
case "bmp":
|
||||
opts := &gobmp.EncoderOptions{}
|
||||
opts.SupportTransparency(false)
|
||||
err = gobmp.EncodeWithOptions(w, imageToPaletted(img), opts)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("imageEncode: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
package cbconvert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/fvbommel/sortorder"
|
||||
"github.com/gen2brain/go-fitz"
|
||||
)
|
||||
|
||||
// coverArchive extracts cover from archive.
|
||||
func (c *Converter) coverArchive(fileName string) (image.Image, error) {
|
||||
var images []string
|
||||
|
||||
contents, err := c.archiveList(fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverArchive: %w", err)
|
||||
}
|
||||
|
||||
for _, ct := range contents {
|
||||
if isImage(ct) {
|
||||
images = append(images, ct)
|
||||
}
|
||||
}
|
||||
|
||||
cover := c.coverName(images)
|
||||
|
||||
data, err := c.archiveFile(fileName, cover)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverArchive: %w", err)
|
||||
}
|
||||
|
||||
var img image.Image
|
||||
img, err = c.imageDecode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverArchive: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// coverDocument extracts cover from document.
|
||||
func (c *Converter) coverDocument(fileName string) (image.Image, error) {
|
||||
doc, err := fitz.New(fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverDocument: %w", err)
|
||||
}
|
||||
defer doc.Close()
|
||||
|
||||
img, err := c.renderPage(doc, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverDocument: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// coverDirectory extracts cover from directory.
|
||||
func (c *Converter) coverDirectory(dir string) (image.Image, error) {
|
||||
contents, err := imagesFromPath(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverDirectory: %w", err)
|
||||
}
|
||||
|
||||
images := imagesFromSlice(contents)
|
||||
cover := c.coverName(images)
|
||||
|
||||
file, err := os.Open(cover)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverDirectory: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var img image.Image
|
||||
img, err = c.imageDecode(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverDirectory: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// pageArchive extracts the page-th image (natural reading order) from an archive.
|
||||
func (c *Converter) pageArchive(fileName string, page int) (image.Image, error) {
|
||||
contents, err := c.archiveList(fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageArchive: %w", err)
|
||||
}
|
||||
|
||||
images := imagesFromSlice(contents)
|
||||
sort.Sort(sortorder.Natural(images))
|
||||
|
||||
if page < 0 || page >= len(images) {
|
||||
return nil, fmt.Errorf("pageArchive: page %d out of range (%d pages)", page+1, len(images))
|
||||
}
|
||||
|
||||
data, err := c.archiveFile(fileName, images[page])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageArchive: %w", err)
|
||||
}
|
||||
|
||||
img, err := c.imageDecode(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageArchive: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// pageDocument extracts the page-th rendered page from a document.
|
||||
func (c *Converter) pageDocument(fileName string, page int) (image.Image, error) {
|
||||
doc, err := fitz.New(fileName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageDocument: %w", err)
|
||||
}
|
||||
defer doc.Close()
|
||||
|
||||
if page < 0 || page >= doc.NumPage() {
|
||||
return nil, fmt.Errorf("pageDocument: page %d out of range (%d pages)", page+1, doc.NumPage())
|
||||
}
|
||||
|
||||
img, err := c.renderPage(doc, page)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageDocument: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// pageDirectory extracts the page-th image (natural reading order) from a directory.
|
||||
func (c *Converter) pageDirectory(dir string, page int) (image.Image, error) {
|
||||
contents, err := imagesFromPath(dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageDirectory: %w", err)
|
||||
}
|
||||
|
||||
images := imagesFromSlice(contents)
|
||||
sort.Sort(sortorder.Natural(images))
|
||||
|
||||
if page < 0 || page >= len(images) {
|
||||
return nil, fmt.Errorf("pageDirectory: page %d out of range (%d pages)", page+1, len(images))
|
||||
}
|
||||
|
||||
file, err := os.Open(images[page])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageDirectory: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
img, err := c.imageDecode(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageDirectory: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// pageImage returns the page-th image of a comic file, document or directory.
|
||||
func (c *Converter) pageImage(fileName string, fileInfo os.FileInfo, page int) (image.Image, error) {
|
||||
var err error
|
||||
var img image.Image
|
||||
|
||||
switch {
|
||||
case fileInfo.IsDir():
|
||||
img, err = c.pageDirectory(fileName, page)
|
||||
case isDocument(fileName):
|
||||
img, err = c.pageDocument(fileName, page)
|
||||
case isArchive(fileName):
|
||||
img, err = c.pageArchive(fileName, page)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pageImage: %w", err)
|
||||
}
|
||||
|
||||
return img, nil
|
||||
}
|
||||
|
||||
// PageCount returns the number of pages (images) in a comic file, document or directory.
|
||||
func (c *Converter) PageCount(fileName string, fileInfo os.FileInfo) (int, error) {
|
||||
switch {
|
||||
case fileInfo.IsDir():
|
||||
contents, err := imagesFromPath(fileName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("PageCount: %w", err)
|
||||
}
|
||||
|
||||
return len(imagesFromSlice(contents)), nil
|
||||
case isDocument(fileName):
|
||||
doc, err := fitz.New(fileName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("PageCount: %w", err)
|
||||
}
|
||||
defer doc.Close()
|
||||
|
||||
return doc.NumPage(), nil
|
||||
case isArchive(fileName):
|
||||
contents, err := c.archiveList(fileName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("PageCount: %w", err)
|
||||
}
|
||||
|
||||
return len(imagesFromSlice(contents)), nil
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// coverName returns the filename that is the most likely to be the cover.
|
||||
func (c *Converter) coverName(images []string) string {
|
||||
if len(images) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
lower := make([]string, 0)
|
||||
for idx, img := range images {
|
||||
img = strings.ToLower(img)
|
||||
lower = append(lower, img)
|
||||
ext := baseNoExt(img)
|
||||
|
||||
if strings.HasPrefix(img, "cover") || strings.HasPrefix(img, "front") ||
|
||||
strings.HasSuffix(ext, "cover") || strings.HasSuffix(ext, "front") {
|
||||
return filepath.ToSlash(images[idx])
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(sortorder.Natural(lower))
|
||||
cover := lower[0]
|
||||
|
||||
for idx, img := range images {
|
||||
img = strings.ToLower(img)
|
||||
if img == cover {
|
||||
return filepath.ToSlash(images[idx])
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// coverImage returns cover as image.Image.
|
||||
func (c *Converter) coverImage(fileName string, fileInfo os.FileInfo) (image.Image, error) {
|
||||
var err error
|
||||
var cover image.Image
|
||||
|
||||
switch {
|
||||
case fileInfo.IsDir():
|
||||
cover, err = c.coverDirectory(fileName)
|
||||
case isDocument(fileName):
|
||||
cover, err = c.coverDocument(fileName)
|
||||
case isArchive(fileName):
|
||||
cover, err = c.coverArchive(fileName)
|
||||
}
|
||||
|
||||
if c.OnProgress != nil {
|
||||
c.OnProgress()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("coverImage: %w", err)
|
||||
}
|
||||
|
||||
return cover, nil
|
||||
}
|
||||
@@ -2,41 +2,12 @@ package cbconvert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// imageToRGBA converts an image.Image to *image.RGBA.
|
||||
func imageToRGBA(src image.Image) *image.RGBA {
|
||||
if dst, ok := src.(*image.RGBA); ok {
|
||||
return dst
|
||||
}
|
||||
|
||||
b := src.Bounds()
|
||||
dst := image.NewRGBA(b)
|
||||
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// imageToGray converts an image.Image to *image.Gray.
|
||||
func imageToGray(src image.Image) *image.Gray {
|
||||
if dst, ok := src.(*image.Gray); ok {
|
||||
return dst
|
||||
}
|
||||
|
||||
b := src.Bounds()
|
||||
dst := image.NewGray(b)
|
||||
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// imagesFromPath returns list of found image files for given directory.
|
||||
func imagesFromPath(path string) ([]string, error) {
|
||||
var images []string
|
||||
@@ -88,7 +59,7 @@ func imagesFromSlice(files []string) []string {
|
||||
return images
|
||||
}
|
||||
|
||||
// isArchive checks if file is archive.
|
||||
// isArchive checks if a file is archive.
|
||||
func isArchive(f string) bool {
|
||||
var types = []string{".rar", ".zip", ".7z", ".tar", ".cbr", ".cbz", ".cb7", ".cbt"}
|
||||
for _, t := range types {
|
||||
@@ -102,7 +73,7 @@ func isArchive(f string) bool {
|
||||
|
||||
// isDocument checks if file is document.
|
||||
func isDocument(f string) bool {
|
||||
var types = []string{".pdf", ".xps", ".epub", ".mobi"}
|
||||
var types = []string{".pdf", ".xps", ".epub", ".mobi", ".docx", ".pptx", ".xlsx"}
|
||||
for _, t := range types {
|
||||
if strings.ToLower(filepath.Ext(f)) == t {
|
||||
return true
|
||||
@@ -147,21 +118,17 @@ func isSize(a, b int64) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// isGrayScale checks if image is grayscale.
|
||||
func isGrayScale(img image.Image) bool {
|
||||
model := img.ColorModel()
|
||||
if model == color.GrayModel || model == color.Gray16Model {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// baseNoExt returns base name without extension.
|
||||
func baseNoExt(filename string) string {
|
||||
return strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename))
|
||||
}
|
||||
|
||||
// flatName flattens a path into a single collision-free name by replacing separators.
|
||||
func flatName(name string) string {
|
||||
name = strings.ReplaceAll(name, "\\", "/")
|
||||
return strings.ReplaceAll(name, "/", "_")
|
||||
}
|
||||
|
||||
// copyFile copies reader to file.
|
||||
func copyFile(reader io.Reader, filename string) error {
|
||||
err := os.MkdirAll(filepath.Dir(filename), 0755)
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
package cbconvert
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"math"
|
||||
|
||||
"github.com/anthonynsimon/bild/adjust"
|
||||
"github.com/anthonynsimon/bild/transform"
|
||||
)
|
||||
|
||||
// Resample filters.
|
||||
const (
|
||||
// NearestNeighbor is the fastest resampling filter, no antialiasing.
|
||||
nearestNeighbor int = iota
|
||||
// Box filter (averaging pixels).
|
||||
box
|
||||
// Linear is the bilinear filter, smooth and reasonably fast.
|
||||
linear
|
||||
// MitchellNetravali is a smooth bicubic filter.
|
||||
mitchellNetravali
|
||||
// CatmullRom is a sharp bicubic filter.
|
||||
catmullRom
|
||||
// Gaussian is a blurring filter, which uses gaussian function, useful for noise removal.
|
||||
gaussian
|
||||
// Lanczos is a high-quality resampling filter, it's slower than cubic filters.
|
||||
lanczos
|
||||
)
|
||||
|
||||
var filters = map[int]transform.ResampleFilter{
|
||||
nearestNeighbor: transform.NearestNeighbor,
|
||||
box: transform.Box,
|
||||
linear: transform.Linear,
|
||||
mitchellNetravali: transform.MitchellNetravali,
|
||||
catmullRom: transform.CatmullRom,
|
||||
gaussian: transform.Gaussian,
|
||||
lanczos: transform.Lanczos,
|
||||
}
|
||||
|
||||
// resampleFilter returns the resample filter for index i, falling back to Linear for an unknown index.
|
||||
func resampleFilter(i int) transform.ResampleFilter {
|
||||
if f, ok := filters[i]; ok {
|
||||
return f
|
||||
}
|
||||
|
||||
return filters[linear]
|
||||
}
|
||||
|
||||
func resize(img image.Image, width, height int, filter transform.ResampleFilter) *image.RGBA {
|
||||
dstW, dstH := width, height
|
||||
|
||||
srcW := img.Bounds().Dx()
|
||||
srcH := img.Bounds().Dy()
|
||||
|
||||
if dstW == 0 {
|
||||
tmpW := float64(dstH) * float64(srcW) / float64(srcH)
|
||||
dstW = int(math.Max(1.0, math.Floor(tmpW+0.5)))
|
||||
}
|
||||
if dstH == 0 {
|
||||
tmpH := float64(dstW) * float64(srcH) / float64(srcW)
|
||||
dstH = int(math.Max(1.0, math.Floor(tmpH+0.5)))
|
||||
}
|
||||
|
||||
if srcW == dstW && srcH == dstH {
|
||||
return imageToRGBA(img)
|
||||
}
|
||||
|
||||
return transform.Resize(img, dstW, dstH, filter)
|
||||
}
|
||||
|
||||
func fit(img image.Image, width, height int, filter transform.ResampleFilter) *image.RGBA {
|
||||
maxW, maxH := width, height
|
||||
|
||||
b := img.Bounds()
|
||||
srcW := b.Dx()
|
||||
srcH := b.Dy()
|
||||
|
||||
if srcW <= maxW && srcH <= maxH {
|
||||
return imageToRGBA(img)
|
||||
}
|
||||
|
||||
srcAspectRatio := float64(srcW) / float64(srcH)
|
||||
maxAspectRatio := float64(maxW) / float64(maxH)
|
||||
|
||||
var dstW, dstH int
|
||||
if srcAspectRatio > maxAspectRatio {
|
||||
dstW = maxW
|
||||
dstH = int(float64(dstW) / srcAspectRatio)
|
||||
} else {
|
||||
dstH = maxH
|
||||
dstW = int(float64(dstH) * srcAspectRatio)
|
||||
}
|
||||
|
||||
return resize(img, dstW, dstH, filter)
|
||||
}
|
||||
|
||||
// withinBounds reports whether img already fits within width by height; a zero dimension is unbounded.
|
||||
func withinBounds(img image.Image, width, height int) bool {
|
||||
b := img.Bounds()
|
||||
|
||||
return (width == 0 || b.Dx() <= width) && (height == 0 || b.Dy() <= height)
|
||||
}
|
||||
|
||||
// resizeFit resizes img to the configured width/height, honoring Fit and NoUpscale.
|
||||
func (c *Converter) resizeFit(img image.Image) image.Image {
|
||||
if c.Opts.Fit {
|
||||
return fit(img, c.Opts.Width, c.Opts.Height, resampleFilter(c.Opts.Filter))
|
||||
}
|
||||
|
||||
if c.Opts.NoUpscale && withinBounds(img, c.Opts.Width, c.Opts.Height) {
|
||||
return img
|
||||
}
|
||||
|
||||
return resize(img, c.Opts.Width, c.Opts.Height, resampleFilter(c.Opts.Filter))
|
||||
}
|
||||
|
||||
func rotate(img image.Image, angle float64) *image.RGBA {
|
||||
return transform.Rotate(img, angle, &transform.RotationOptions{ResizeBounds: true, Pivot: &image.Point{}})
|
||||
}
|
||||
|
||||
func brightness(img image.Image, change float64) *image.RGBA {
|
||||
return adjust.Brightness(img, change/100)
|
||||
}
|
||||
|
||||
func contrast(img image.Image, change float64) *image.RGBA {
|
||||
return adjust.Contrast(img, change/100)
|
||||
}
|
||||
|
||||
// imageToRGBA converts an image.Image to *image.RGBA.
|
||||
func imageToRGBA(src image.Image) *image.RGBA {
|
||||
if dst, ok := src.(*image.RGBA); ok {
|
||||
return dst
|
||||
}
|
||||
|
||||
b := src.Bounds()
|
||||
dst := image.NewRGBA(b)
|
||||
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// imageToGray converts an image.Image to *image.Gray.
|
||||
func imageToGray(src image.Image) *image.Gray {
|
||||
if dst, ok := src.(*image.Gray); ok {
|
||||
return dst
|
||||
}
|
||||
|
||||
b := src.Bounds()
|
||||
dst := image.NewGray(b)
|
||||
draw.Draw(dst, dst.Bounds(), src, b.Min, draw.Src)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// isGrayScale checks if the image is grayscale.
|
||||
func isGrayScale(img image.Image) bool {
|
||||
model := img.ColorModel()
|
||||
if model == color.GrayModel || model == color.Gray16Model {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
var colors16 = []color.Color{
|
||||
color.RGBA{A: 255},
|
||||
color.RGBA{R: 17, G: 17, B: 17, A: 255},
|
||||
color.RGBA{R: 34, G: 34, B: 34, A: 255},
|
||||
color.RGBA{R: 51, G: 51, B: 51, A: 255},
|
||||
color.RGBA{R: 68, G: 68, B: 68, A: 255},
|
||||
color.RGBA{R: 85, G: 85, B: 85, A: 255},
|
||||
color.RGBA{R: 102, G: 102, B: 102, A: 255},
|
||||
color.RGBA{R: 119, G: 119, B: 119, A: 255},
|
||||
color.RGBA{R: 136, G: 136, B: 136, A: 255},
|
||||
color.RGBA{R: 153, G: 153, B: 153, A: 255},
|
||||
color.RGBA{R: 170, G: 170, B: 170, A: 255},
|
||||
color.RGBA{R: 187, G: 187, B: 187, A: 255},
|
||||
color.RGBA{R: 204, G: 204, B: 204, A: 255},
|
||||
color.RGBA{R: 221, G: 221, B: 221, A: 255},
|
||||
color.RGBA{R: 238, G: 238, B: 238, A: 255},
|
||||
color.RGBA{R: 255, G: 255, B: 255, A: 255},
|
||||
}
|
||||
|
||||
// imageToPaletted converts an image.Image to *image.Paletted using 16-color palette.
|
||||
func imageToPaletted(src image.Image) *image.Paletted {
|
||||
b := src.Bounds()
|
||||
dst := image.NewPaletted(b, colors16)
|
||||
draw.FloydSteinberg.Draw(dst, b, imageToGray(src), b.Min)
|
||||
|
||||
return dst
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package cbconvert
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"image"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -18,9 +21,6 @@ func TestConvert(t *testing.T) {
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
conv.Initialize()
|
||||
defer conv.Terminate()
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test", "testdata"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -32,7 +32,7 @@ func TestConvert(t *testing.T) {
|
||||
for _, file := range files {
|
||||
conv.Opts.Suffix = fmt.Sprintf("_%s%s", format, filepath.Ext(file.Path))
|
||||
|
||||
err = conv.Convert(file.Path, file.Stat)
|
||||
err = conv.Convert(file)
|
||||
if err != nil {
|
||||
t.Errorf("format %s: file %s: %v", format, file.Name, err)
|
||||
}
|
||||
@@ -56,16 +56,13 @@ func TestCover(t *testing.T) {
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
conv.Initialize()
|
||||
defer conv.Terminate()
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbt"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
err = conv.Cover(file.Path, file.Stat)
|
||||
err = conv.Cover(file)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -88,16 +85,13 @@ func TestThumbnail(t *testing.T) {
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
conv.Initialize()
|
||||
defer conv.Terminate()
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.pdf"})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
err = conv.Thumbnail(file.Path, file.Stat)
|
||||
err = conv.Thumbnail(file)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -108,3 +102,713 @@ func TestThumbnail(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArgs(t *testing.T) {
|
||||
opts := NewOptions()
|
||||
if got := opts.Args(); len(got) != 0 {
|
||||
t.Errorf("defaults should emit no flags, got %v", got)
|
||||
}
|
||||
|
||||
opts.Format = "webp"
|
||||
opts.Quality = 90
|
||||
opts.Effort = 4
|
||||
opts.Lossless = true
|
||||
opts.Width = 1200
|
||||
opts.NoUpscale = true
|
||||
opts.DPI = 150
|
||||
opts.Grayscale = true
|
||||
opts.OutDir = "/out"
|
||||
|
||||
got := strings.Join(opts.Args(), " ")
|
||||
want := "--width 1200 --no-upscale --dpi 150 --format webp --quality 90 --effort 4 --lossless --grayscale --outdir /out"
|
||||
if got != want {
|
||||
t.Errorf("Args() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoUpscale(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
width := func(o Options) int {
|
||||
conv := New(o)
|
||||
files, err := conv.Files([]string{"testdata/test.cbz"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return firstPage(t, conv, filepath.Join(tmpDir, "test.cbz")).Bounds().Dx()
|
||||
}
|
||||
|
||||
base := NewOptions()
|
||||
base.OutDir = tmpDir
|
||||
orig := width(base)
|
||||
|
||||
up := NewOptions()
|
||||
up.OutDir = tmpDir
|
||||
up.Width = orig * 2
|
||||
up.NoUpscale = true
|
||||
if got := width(up); got != orig {
|
||||
t.Errorf("NoUpscale should keep original width %d, got %d", orig, got)
|
||||
}
|
||||
|
||||
no := NewOptions()
|
||||
no.OutDir = tmpDir
|
||||
no.Width = orig * 2
|
||||
if got := width(no); got != orig*2 {
|
||||
t.Errorf("without NoUpscale should upscale to %d, got %d", orig*2, got)
|
||||
}
|
||||
|
||||
down := NewOptions()
|
||||
down.OutDir = tmpDir
|
||||
down.Width = orig / 2
|
||||
down.NoUpscale = true
|
||||
if got := width(down); got != orig/2 {
|
||||
t.Errorf("NoUpscale should still downscale to %d, got %d", orig/2, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertDPI(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
dims := func(dpi int) int {
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.DPI = dpi
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.pdf"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
return firstPage(t, conv, filepath.Join(tmpDir, "test.cbz")).Bounds().Dx()
|
||||
}
|
||||
|
||||
low := dims(150)
|
||||
high := dims(600)
|
||||
if low >= high {
|
||||
t.Errorf("higher DPI should render larger pages: 150dpi=%d, 600dpi=%d", low, high)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewPage(t *testing.T) {
|
||||
for _, name := range []string{"testdata/test.cbz", "testdata/test.pdf"} {
|
||||
conv := New(NewOptions())
|
||||
|
||||
files, err := conv.Files([]string{name})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("%s: expected 1 file, got %d", name, len(files))
|
||||
}
|
||||
file := files[0]
|
||||
|
||||
n, err := conv.PageCount(file.Path, file.Stat)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if n < 2 {
|
||||
t.Fatalf("%s: expected >= 2 pages, got %d", name, n)
|
||||
}
|
||||
|
||||
for _, page := range []int{0, 1, n - 1} {
|
||||
img, err := conv.PreviewPage(file.Path, file.Stat, page, 0, 0)
|
||||
if err != nil || img.Image == nil {
|
||||
t.Fatalf("%s: page %d: img=%v err=%v", name, page, img.Image, err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := conv.PreviewPage(file.Path, file.Stat, n, 0, 0); err == nil {
|
||||
t.Errorf("%s: page %d (out of range) should error", name, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertResize(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.Width = 100
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbz"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
img := firstPage(t, conv, filepath.Join(tmpDir, "test.cbz"))
|
||||
if got := img.Bounds().Dx(); got != 100 {
|
||||
t.Errorf("resized width: got %d, want 100", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFit(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.Width = 120
|
||||
opts.Height = 120
|
||||
opts.Fit = true
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbz"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
img := firstPage(t, conv, filepath.Join(tmpDir, "test.cbz"))
|
||||
w, h := img.Bounds().Dx(), img.Bounds().Dy()
|
||||
if w > 120 || h > 120 {
|
||||
t.Errorf("fit exceeded bounds: got %dx%d, want <= 120x120", w, h)
|
||||
}
|
||||
if w != 120 && h != 120 {
|
||||
t.Errorf("fit did not touch a bound: got %dx%d, want one side == 120", w, h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertTar(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.Archive = "tar"
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbz"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
out := filepath.Join(tmpDir, "test.cbt")
|
||||
list, err := conv.archiveList(out)
|
||||
if err != nil {
|
||||
t.Fatalf("read tar output: %v", err)
|
||||
}
|
||||
if len(list) != 2 {
|
||||
t.Errorf("expected 2 pages in tar output, got %d: %v", len(list), list)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZipLevel(t *testing.T) {
|
||||
convertWith := func(level int) *zip.ReadCloser {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { os.RemoveAll(tmpDir) })
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.ZipLevel = level
|
||||
opts.NoConvert = true
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbz"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
zr, err := zip.OpenReader(filepath.Join(tmpDir, "test.cbz"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { zr.Close() })
|
||||
|
||||
return zr
|
||||
}
|
||||
|
||||
store := convertWith(0)
|
||||
for _, f := range store.File {
|
||||
if f.Method != zip.Store {
|
||||
t.Errorf("level 0: %s stored with method %d, want Store", f.Name, f.Method)
|
||||
}
|
||||
if f.CompressedSize64 != f.UncompressedSize64 {
|
||||
t.Errorf("level 0: %s is compressed (%d < %d)", f.Name, f.CompressedSize64, f.UncompressedSize64)
|
||||
}
|
||||
}
|
||||
|
||||
deflate := convertWith(9)
|
||||
for _, f := range deflate.File {
|
||||
if f.Method != zip.Deflate {
|
||||
t.Errorf("level 9: %s method %d, want Deflate", f.Name, f.Method)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageTransforms(t *testing.T) {
|
||||
conv := New(NewOptions())
|
||||
|
||||
f, err := os.Open("testdata/test/00.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
src, err := conv.imageDecode(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
srcW, srcH := src.Bounds().Dx(), src.Bounds().Dy()
|
||||
|
||||
conv.Opts.Rotate = 90
|
||||
rotated := conv.imageTransform(src)
|
||||
if rotated.Bounds().Dx() != srcH || rotated.Bounds().Dy() != srcW {
|
||||
t.Errorf("rotate 90: got %dx%d, want %dx%d", rotated.Bounds().Dx(), rotated.Bounds().Dy(), srcH, srcW)
|
||||
}
|
||||
|
||||
conv.Opts = NewOptions()
|
||||
conv.Opts.Grayscale = true
|
||||
gray := conv.imageTransform(src)
|
||||
if !isGrayScale(gray) {
|
||||
t.Errorf("grayscale: result is not grayscale")
|
||||
}
|
||||
|
||||
conv.Opts = NewOptions()
|
||||
conv.Opts.Brightness = 20
|
||||
conv.Opts.Contrast = 20
|
||||
adjusted := conv.imageTransform(src)
|
||||
if adjusted.Bounds().Dx() != srcW || adjusted.Bounds().Dy() != srcH {
|
||||
t.Errorf("brightness/contrast changed dimensions: got %dx%d, want %dx%d",
|
||||
adjusted.Bounds().Dx(), adjusted.Bounds().Dy(), srcW, srcH)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoverName(t *testing.T) {
|
||||
conv := New(NewOptions())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
images []string
|
||||
want string
|
||||
}{
|
||||
{"empty", nil, ""},
|
||||
{"natural sort", []string{"10.jpg", "2.jpg", "1.jpg"}, "1.jpg"},
|
||||
{"cover prefix wins", []string{"01.jpg", "cover.jpg", "02.jpg"}, "cover.jpg"},
|
||||
{"front prefix wins", []string{"01.jpg", "front.png", "00.jpg"}, "front.png"},
|
||||
{"cover suffix wins", []string{"01.jpg", "page_cover.jpg"}, "page_cover.jpg"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := conv.coverName(tt.images); got != tt.want {
|
||||
t.Errorf("coverName(%v) = %q, want %q", tt.images, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoverDirectory(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("expected 1 directory file, got %d", len(files))
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Cover(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(tmpDir, "test.jpg")); err != nil {
|
||||
t.Errorf("directory cover not written: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileType(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
want string
|
||||
}{
|
||||
{"testdata/test.cbz", "ZIP"},
|
||||
{"testdata/test.cbr", "RAR"},
|
||||
{"testdata/test.cb7", "7Z"},
|
||||
{"testdata/test.cbt", "TAR"},
|
||||
{"testdata/test.pdf", "PDF"},
|
||||
{"testdata/test", "DIR"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.path, func(t *testing.T) {
|
||||
if got := FileType(tt.path); got != tt.want {
|
||||
t.Errorf("FileType(%q) = %q, want %q", tt.path, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCombine(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
opts.OutFile = "merged"
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{"testdata/test.cbz", "testdata/test.cbt"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(files) != 2 {
|
||||
t.Fatalf("expected 2 input files, got %d", len(files))
|
||||
}
|
||||
|
||||
if err := conv.Combine(files); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
zr, err := zip.OpenReader(filepath.Join(tmpDir, "merged.cbz"))
|
||||
if err != nil {
|
||||
t.Fatalf("open combined archive: %v", err)
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
var names []string
|
||||
for _, f := range zr.File {
|
||||
names = append(names, f.Name)
|
||||
}
|
||||
|
||||
if len(names) != 4 {
|
||||
t.Fatalf("expected 4 pages in combined archive, got %d: %v", len(names), names)
|
||||
}
|
||||
|
||||
// each input is prefixed so identically named pages do not collide
|
||||
var first, second int
|
||||
for _, n := range names {
|
||||
switch {
|
||||
case strings.HasPrefix(n, "0001_"):
|
||||
first++
|
||||
case strings.HasPrefix(n, "0002_"):
|
||||
second++
|
||||
}
|
||||
}
|
||||
if first != 2 || second != 2 {
|
||||
t.Errorf("expected 2 pages from each input, got 0001_=%d 0002_=%d: %v", first, second, names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubfolders(t *testing.T) {
|
||||
page0, err := os.ReadFile("testdata/test/00.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
page1, err := os.ReadFile("testdata/test/01.jpg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
inDir, err := os.MkdirTemp(os.TempDir(), "cbc-in")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(inDir)
|
||||
|
||||
src := filepath.Join(inDir, "chapters.cbz")
|
||||
buildZip(t, src, []zipEntry{
|
||||
{"chapter1/00.jpg", page0},
|
||||
{"chapter1/01.jpg", page1},
|
||||
{"chapter2/00.jpg", page0},
|
||||
{"chapter2/01.jpg", page1},
|
||||
})
|
||||
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc-out")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = tmpDir
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{src})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
zr, err := zip.OpenReader(filepath.Join(tmpDir, "chapters.cbz"))
|
||||
if err != nil {
|
||||
t.Fatalf("open output archive: %v", err)
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
// without subfolder preservation chapter2/00 overwrites chapter1/00 and only 2 pages survive
|
||||
if len(zr.File) != 4 {
|
||||
var names []string
|
||||
for _, f := range zr.File {
|
||||
names = append(names, f.Name)
|
||||
}
|
||||
t.Fatalf("expected 4 pages from numbered subfolders, got %d: %v", len(zr.File), names)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeta(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp(os.TempDir(), "cbc")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// operate on a copy so the fixture stays intact
|
||||
data, err := os.ReadFile("testdata/test.cbz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
archive := filepath.Join(tmpDir, "meta.cbz")
|
||||
if err := os.WriteFile(archive, data, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
conv := New(NewOptions())
|
||||
|
||||
conv.Opts = NewOptions()
|
||||
conv.Opts.CommentBody = "hello world"
|
||||
if _, err := conv.Meta(archive); err != nil {
|
||||
t.Fatalf("set comment: %v", err)
|
||||
}
|
||||
|
||||
conv.Opts = NewOptions()
|
||||
conv.Opts.Comment = true
|
||||
got, err := conv.Meta(archive)
|
||||
if err != nil {
|
||||
t.Fatalf("get comment: %v", err)
|
||||
}
|
||||
if got != "hello world" {
|
||||
t.Errorf("comment roundtrip: got %q, want %q", got, "hello world")
|
||||
}
|
||||
|
||||
extra := filepath.Join(tmpDir, "ComicInfo.xml")
|
||||
if err := os.WriteFile(extra, []byte("<ComicInfo/>"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
conv.Opts = NewOptions()
|
||||
conv.Opts.FileAdd = extra
|
||||
if _, err := conv.Meta(archive); err != nil {
|
||||
t.Fatalf("add file: %v", err)
|
||||
}
|
||||
if !archiveHas(t, conv, archive, "ComicInfo.xml") {
|
||||
t.Errorf("added file not found in archive")
|
||||
}
|
||||
|
||||
conv.Opts = NewOptions()
|
||||
conv.Opts.FileRemove = "ComicInfo.xml"
|
||||
if _, err := conv.Meta(archive); err != nil {
|
||||
t.Fatalf("remove file: %v", err)
|
||||
}
|
||||
if archiveHas(t, conv, archive, "ComicInfo.xml") {
|
||||
t.Errorf("removed file still present in archive")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecursive(t *testing.T) {
|
||||
inDir, err := os.MkdirTemp(os.TempDir(), "cbc-in")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(inDir)
|
||||
|
||||
sub := filepath.Join(inDir, "chapter1")
|
||||
if err := os.MkdirAll(sub, 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
src, err := os.ReadFile("testdata/test.cbz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(sub, "test.cbz"), src, 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
outDir, err := os.MkdirTemp(os.TempDir(), "cbc-out")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(outDir)
|
||||
|
||||
opts := NewOptions()
|
||||
opts.OutDir = outDir
|
||||
opts.Recursive = true
|
||||
|
||||
conv := New(opts)
|
||||
|
||||
files, err := conv.Files([]string{inDir})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(files) != 1 {
|
||||
t.Fatalf("expected 1 file, got %d", len(files))
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// output must mirror the input subtree relative to the input root, not the absolute path
|
||||
want := filepath.Join(outDir, "chapter1", "test.cbz")
|
||||
if _, err := os.Stat(want); err != nil {
|
||||
t.Errorf("expected output relative to input root at %s: %v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
type zipEntry struct {
|
||||
name string
|
||||
data []byte
|
||||
}
|
||||
|
||||
func buildZip(t *testing.T, path string, entries []zipEntry) {
|
||||
t.Helper()
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
zw := zip.NewWriter(f)
|
||||
for _, e := range entries {
|
||||
w, err := zw.Create(e.name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w.Write(e.data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := zw.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func firstPage(t *testing.T, conv *Converter, archive string) image.Image {
|
||||
t.Helper()
|
||||
|
||||
zr, err := zip.OpenReader(archive)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer zr.Close()
|
||||
|
||||
if len(zr.File) == 0 {
|
||||
t.Fatalf("archive %s has no entries", archive)
|
||||
}
|
||||
|
||||
rc, err := zr.File[0].Open()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
img, err := conv.imageDecode(rc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
func archiveHas(t *testing.T, conv *Converter, archive, name string) bool {
|
||||
t.Helper()
|
||||
|
||||
list, err := conv.archiveList(archive)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, n := range list {
|
||||
if n == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/cbconvert"
|
||||
"github.com/gen2brain/cbconvert/cmd/cbconvert-gui/i18n"
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
func fileDlg(title string, multiple, directory bool, dirKey string) ([]string, error) {
|
||||
ret := make([]string, 0)
|
||||
|
||||
dlg := iup.FileDlg()
|
||||
defer dlg.Destroy()
|
||||
|
||||
if !directory {
|
||||
mf := "YES"
|
||||
if !multiple {
|
||||
mf = "NO"
|
||||
}
|
||||
|
||||
dlg.SetAttributes(map[string]string{
|
||||
"DIALOGTYPE": "OPEN",
|
||||
"MULTIPLEFILES": mf,
|
||||
"MULTIVALUEPATH": "YES",
|
||||
"EXTFILTER": "Comic Files|*.rar;*.zip;*.7z;*.tar;*.cbr;*.cbz;*.cb7;*.cbt;*.pdf;*.epub;*.mobi;*.docx;*.pptx|",
|
||||
"FILTER": "*.cb*", // for Motif
|
||||
"TITLE": title,
|
||||
"SHOWPREVIEW": "YES",
|
||||
"PREVIEWWIDTH": "240",
|
||||
"PREVIEWHEIGHT": "320",
|
||||
})
|
||||
|
||||
dlg.SetCallback("FILE_CB", iup.FileFunc(previewCover()))
|
||||
} else {
|
||||
dlg.SetAttributes(map[string]string{
|
||||
"DIALOGTYPE": "DIR",
|
||||
"TITLE": title,
|
||||
})
|
||||
}
|
||||
|
||||
setStartDir(dlg, dirKey)
|
||||
|
||||
iup.Popup(dlg, iup.CENTERPARENT, iup.CENTERPARENT)
|
||||
|
||||
if dlg.GetInt("STATUS") == 0 {
|
||||
switch {
|
||||
case multiple:
|
||||
// MULTIVALUEPATH makes each MULTIVALUE a full path (id 0 is the path), so a folder-spanning selection works.
|
||||
count := dlg.GetInt("MULTIVALUECOUNT")
|
||||
if count > 1 {
|
||||
for i := 1; i < count; i++ {
|
||||
ret = append(ret, iup.GetAttributeId(dlg, "MULTIVALUE", i))
|
||||
}
|
||||
} else if value := dlg.GetAttribute("VALUE"); value != "" {
|
||||
ret = append(ret, value)
|
||||
}
|
||||
default:
|
||||
ret = append(ret, dlg.GetAttribute("VALUE"))
|
||||
}
|
||||
|
||||
rememberDir(dlg, dirKey)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
const dlgPreviewName = "_FILEDLGPREVIEW_"
|
||||
|
||||
// previewPad insets the cover from the preview pane edges, in pixels per side.
|
||||
const previewPad = 8
|
||||
|
||||
// previewCover returns a FILE_CB handler that draws the highlighted comic's cover in the dialog preview pane.
|
||||
// Extracted covers are cached by path so re-highlighting a file doesn't re-extract it.
|
||||
func previewCover() iup.FileFunc {
|
||||
const maxCache = 32
|
||||
|
||||
cache := make(map[string]iup.Ihandle)
|
||||
order := make([]string, 0, maxCache)
|
||||
|
||||
cover := func(path string, w, h int) iup.Ihandle {
|
||||
if img, ok := cache[path]; ok {
|
||||
return img
|
||||
}
|
||||
|
||||
img := loadCover(path, w, h)
|
||||
cache[path] = img
|
||||
order = append(order, path)
|
||||
|
||||
if len(order) > maxCache {
|
||||
old := order[0]
|
||||
order = order[1:]
|
||||
if oi := cache[old]; oi != 0 {
|
||||
oi.Destroy()
|
||||
}
|
||||
delete(cache, old)
|
||||
}
|
||||
|
||||
return img
|
||||
}
|
||||
|
||||
return func(ih iup.Ihandle, filename, status string) int {
|
||||
switch status {
|
||||
case "PAINT":
|
||||
iup.DrawBegin(ih)
|
||||
cw, ch := iup.DrawGetSize(ih)
|
||||
iup.DrawParentBackground(ih)
|
||||
|
||||
if image := cover(filename, cw-2*previewPad, ch-2*previewPad); image != 0 {
|
||||
iup.SetHandle(dlgPreviewName, image)
|
||||
iw, iih, _ := iup.DrawGetImageInfo(dlgPreviewName)
|
||||
iup.DrawImage(ih, dlgPreviewName, (cw-iw)/2, (ch-iih)/2, iw, iih)
|
||||
} else {
|
||||
ih.SetAttribute("DRAWCOLOR", "128 128 128")
|
||||
noPreview := i18n.Str(i18n.NoPreview)
|
||||
tw, th := iup.DrawGetTextSize(ih, noPreview)
|
||||
iup.DrawText(ih, noPreview, (cw-tw)/2, (ch-th)/2, 0, 0)
|
||||
}
|
||||
|
||||
iup.DrawEnd(ih)
|
||||
case "FINISH":
|
||||
for _, img := range cache {
|
||||
if img != 0 {
|
||||
img.Destroy()
|
||||
}
|
||||
}
|
||||
cache = make(map[string]iup.Ihandle)
|
||||
order = order[:0]
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
}
|
||||
|
||||
// loadCover extracts the cover of a comic file and returns it as an IUP image fitted to w by h, or 0.
|
||||
func loadCover(path string, w, h int) iup.Ihandle {
|
||||
if w <= 0 || h <= 0 || !isComic(path) {
|
||||
return 0
|
||||
}
|
||||
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil || fi.IsDir() {
|
||||
return 0
|
||||
}
|
||||
|
||||
opts := cbconvert.NewOptions()
|
||||
opts.DPI = 96
|
||||
|
||||
img, err := cbconvert.New(opts).CoverPreview(path, fi, w, h)
|
||||
if err != nil || img.Image == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return iup.ImageFromImage(img.Image)
|
||||
}
|
||||
|
||||
func isComic(path string) bool {
|
||||
switch strings.ToLower(filepath.Ext(path)) {
|
||||
case ".rar", ".zip", ".7z", ".tar", ".cbr", ".cbz", ".cb7", ".cbt",
|
||||
".pdf", ".xps", ".epub", ".mobi", ".docx", ".pptx", ".xlsx":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func saveDlg(title, dirKey string) string {
|
||||
dlg := iup.FileDlg()
|
||||
defer dlg.Destroy()
|
||||
|
||||
dlg.SetAttributes(map[string]string{
|
||||
"DIALOGTYPE": "SAVE",
|
||||
"EXTFILTER": "Comic Files|*.cbz;*.cbt|",
|
||||
"FILTER": "*.cb*", // for Motif
|
||||
"TITLE": title,
|
||||
})
|
||||
|
||||
setStartDir(dlg, dirKey)
|
||||
|
||||
iup.Popup(dlg, iup.CENTERPARENT, iup.CENTERPARENT)
|
||||
|
||||
if dlg.GetInt("STATUS") == -1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
rememberDir(dlg, dirKey)
|
||||
|
||||
return dlg.GetAttribute("VALUE")
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=CBconvert
|
||||
GenericName=A Comic Book converter
|
||||
Comment=A comic converter with support for .cb*, .pdf, .xps, .epub, .mobi and directories.
|
||||
Exec=cbconvert-gui
|
||||
Icon=cbconvert
|
||||
Terminal=false
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=Graphics;Utility;
|
||||
@@ -1,60 +0,0 @@
|
||||
diff -ur mupdf-1.23.3-source.orig/Makefile mupdf-1.23.3-source/Makefile
|
||||
--- mupdf-1.23.3-source.orig/Makefile 2023-09-05 13:51:19.000000000 +0200
|
||||
+++ mupdf-1.23.3-source/Makefile 2023-09-08 09:28:01.165486441 +0200
|
||||
@@ -3,7 +3,7 @@
|
||||
-include user.make
|
||||
|
||||
ifndef build
|
||||
- build := release
|
||||
+ build := debug
|
||||
endif
|
||||
|
||||
default: all
|
||||
@@ -290,17 +290,19 @@
|
||||
$(THREAD_LIB) : $(THREAD_OBJ)
|
||||
$(PKCS7_LIB) : $(PKCS7_OBJ)
|
||||
else
|
||||
-MUPDF_LIB = $(OUT)/libmupdf.a
|
||||
+MUPDF_LIB = libmupdf.so
|
||||
LIBS_TO_INSTALL_IN_LIB = $(MUPDF_LIB) $(THIRD_LIB)
|
||||
-THIRD_LIB = $(OUT)/libmupdf-third.a
|
||||
+THIRD_LIB =
|
||||
+MUPDF_STATIC = $(OUT)/libmupdf.a
|
||||
ifneq ($(USE_SYSTEM_GLUT),yes)
|
||||
THIRD_GLUT_LIB = $(OUT)/libmupdf-glut.a
|
||||
endif
|
||||
THREAD_LIB = $(OUT)/libmupdf-threads.a
|
||||
PKCS7_LIB = $(OUT)/libmupdf-pkcs7.a
|
||||
|
||||
-$(MUPDF_LIB) : $(MUPDF_OBJ)
|
||||
-$(THIRD_LIB) : $(THIRD_OBJ)
|
||||
+$(MUPDF_LIB) : $(MUPDF_OBJ) $(THIRD_OBJ)
|
||||
+ $(QUIET_LINK) $(CC) $(LDFLAGS) --shared -Wl,-soname -Wl,$(MUPDF_LIB) -o $@ $^ $(THIRD_LIBS) $(LIBS)
|
||||
+$(MUPDF_STATIC): $(MUPDF_OBJ) $(THIRD_OBJ)
|
||||
$(THIRD_GLUT_LIB) : $(THIRD_GLUT_OBJ)
|
||||
$(THREAD_LIB) : $(THREAD_OBJ)
|
||||
$(PKCS7_LIB) : $(PKCS7_OBJ)
|
||||
diff -ur mupdf-1.23.3-source.orig/Makethird mupdf-1.23.3-source/Makethird
|
||||
--- mupdf-1.23.3-source.orig/Makethird 2023-09-05 13:51:19.000000000 +0200
|
||||
+++ mupdf-1.23.3-source/Makethird 2023-09-08 01:16:56.785811250 +0200
|
||||
@@ -2,16 +2,16 @@
|
||||
|
||||
ifeq ($(USE_SYSTEM_LIBS),yes)
|
||||
USE_SYSTEM_FREETYPE := yes
|
||||
- USE_SYSTEM_GUMBO := yes
|
||||
+ USE_SYSTEM_GUMBO := no
|
||||
USE_SYSTEM_HARFBUZZ := yes
|
||||
- USE_SYSTEM_JBIG2DEC := yes
|
||||
+ USE_SYSTEM_JBIG2DEC := no
|
||||
USE_SYSTEM_JPEGXR := no # not available
|
||||
USE_SYSTEM_LCMS2 := no # lcms2mt is strongly preferred
|
||||
USE_SYSTEM_LIBJPEG := yes
|
||||
USE_SYSTEM_MUJS := no # not available
|
||||
- USE_SYSTEM_OPENJPEG := yes
|
||||
+ USE_SYSTEM_OPENJPEG := no
|
||||
USE_SYSTEM_ZLIB := yes
|
||||
- USE_SYSTEM_GLUT := yes
|
||||
+ USE_SYSTEM_GLUT := no
|
||||
USE_SYSTEM_CURL := yes
|
||||
USE_SYSTEM_LEPTONICA := yes
|
||||
USE_SYSTEM_TESSERACT := yes
|
||||
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.gen2brain.cbconvert</id>
|
||||
|
||||
<name>CBconvert</name>
|
||||
<developer_name>Milan Nikolic</developer_name>
|
||||
<summary>A Comic Book converter</summary>
|
||||
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>GPL-3</project_license>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
A comic book converter with support for .cb*, .pdf, .xps, .epub, .mobi and directories.
|
||||
Images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, JXL or 4-Bit BMP (16 colors) file format.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">io.github.gen2brain.cbconvert.desktop</launchable>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/gen2brain/cbconvert/master/cmd/cbconvert-gui/screenshots/linux-02.jpg</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/gen2brain/cbconvert/master/cmd/cbconvert-gui/screenshots/linux-01.jpg</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<url type="homepage">https://github.com/gen2brain/cbconvert</url>
|
||||
<url type="bugtracker">https://github.com/gen2brain/cbconvert/issues</url>
|
||||
|
||||
<releases>
|
||||
<release version="1.0.1" date="2023-09-14" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add flatpak manifest</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.1</url>
|
||||
</release>
|
||||
<release version="1.0.0" date="2023-09-14" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add GUI</li>
|
||||
<li>Add support for JPEG XL</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.0</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
</component>
|
||||
@@ -1,369 +0,0 @@
|
||||
app-id: io.github.gen2brain.cbconvert
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '23.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
sdk-extensions:
|
||||
- org.freedesktop.Sdk.Extension.golang
|
||||
|
||||
command: cbconvert-gui
|
||||
|
||||
finish-args:
|
||||
- --share=ipc
|
||||
- --socket=x11
|
||||
|
||||
build-options:
|
||||
env:
|
||||
- GOROOT=/usr/lib/sdk/golang
|
||||
|
||||
cleanup:
|
||||
- '*.a'
|
||||
- '*.la'
|
||||
- '/bin/Magick*'
|
||||
- '/etc'
|
||||
- '/include'
|
||||
- '/lib/cmake'
|
||||
- '/lib/pkgconfig'
|
||||
- '/lib/ImageMagick*'
|
||||
- '/share'
|
||||
|
||||
modules:
|
||||
|
||||
- name: libheif
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --enable-shared
|
||||
- --disable-static
|
||||
- --disable-libde265
|
||||
- --disable-dav1d
|
||||
- --disable-go
|
||||
- --disable-gdk-pixbuf
|
||||
- --disable-rav1e
|
||||
- --disable-x265
|
||||
- --disable-tests
|
||||
- --disable-examples
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/strukturag/libheif/releases/download/v1.15.2/libheif-1.15.2.tar.gz
|
||||
sha256: 7a4c6077f45180926583e2087571371bdd9cb21b6e6fada85a6fbd544f26a0e2
|
||||
|
||||
- name: highway
|
||||
buildsystem: cmake
|
||||
config-opts:
|
||||
- -DBUILD_SHARED_LIBS=ON
|
||||
- -DHWY_ENABLE_TESTS=OFF
|
||||
- -DHWY_ENABLE_EXAMPLES=OFF
|
||||
- -DHWY_WARNINGS_ARE_ERRORS=OFF
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/google/highway/archive/refs/tags/1.0.7.tar.gz
|
||||
sha256: 5434488108186c170a5e2fca5e3c9b6ef59a1caa4d520b008a9b8be6b8abe6c5
|
||||
|
||||
- name: libjxl
|
||||
buildsystem: cmake
|
||||
config-opts:
|
||||
- -DBUILD_SHARED_LIBS=ON
|
||||
- -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
|
||||
- -DJPEGXL_FORCE_SYSTEM_BROTLI=ON
|
||||
- -DJPEGXL_FORCE_SYSTEM_LCMS2=ON
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/libjxl/libjxl/archive/refs/tags/v0.8.2.tar.gz
|
||||
sha256: c70916fb3ed43784eb840f82f05d390053a558e2da106e40863919238fa7b420
|
||||
|
||||
- name: ImageMagick
|
||||
buildsystem: autotools
|
||||
config-opts:
|
||||
- --enable-shared
|
||||
- --disable-static
|
||||
- --enable-zero-configuration
|
||||
- --without-frozenpaths
|
||||
- --without-utilities
|
||||
- --without-modules
|
||||
- --without-magick-plus-plus
|
||||
- --without-perl
|
||||
- --without-bzlib
|
||||
- --without-x
|
||||
- --without-zip
|
||||
- --without-dps
|
||||
- --without-djvu
|
||||
- --without-autotrace
|
||||
- --without-fftw
|
||||
- --without-fpx
|
||||
- --without-fontconfig
|
||||
- --without-freetype
|
||||
- --without-gslib
|
||||
- --without-gvc
|
||||
- --without-jbig
|
||||
- --without-openjp2
|
||||
- --without-lcms
|
||||
- --without-lqr
|
||||
- --without-lzma
|
||||
- --without-openexr
|
||||
- --without-pango
|
||||
- --without-raw
|
||||
- --without-rsvg
|
||||
- --without-wmf
|
||||
- --without-xml
|
||||
- --disable-hdri
|
||||
- --disable-opencl
|
||||
- --disable-openmp
|
||||
- --with-jpeg
|
||||
- --with-png
|
||||
- --with-tiff
|
||||
- --with-webp
|
||||
- --with-heic
|
||||
- --with-jxl
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/ImageMagick/ImageMagick/archive/refs/tags/7.1.1-15.tar.gz
|
||||
sha256: 2372192a76af9be43c0543dd7ae6dfbf34b11fc0203583453ce3f9f707c36bcc
|
||||
|
||||
- name: MuPDF
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- HAVE_X11=no HAVE_LIBCRYPTO=no HAVE_GLUT=no HAVE_OBJCOPY=no USE_SYSTEM_LIBS=yes build=debug shared=yes tofu=yes tofu_cjk=yes make libs
|
||||
- install -Dm00755 build/shared-debug-tofu-tofu_cjk/libmupdf.so $FLATPAK_DEST/lib/debug/lib/libmupdf.so.debug
|
||||
- install -dm00755 $FLATPAK_DEST/lib/debug/source/mupdf/source
|
||||
- cp -r source $FLATPAK_DEST/lib/debug/source/mupdf
|
||||
- cp -r include/mupdf $FLATPAK_DEST/include/
|
||||
- install -Dsm00755 build/shared-debug-tofu-tofu_cjk/libmupdf.so $FLATPAK_DEST/lib/libmupdf.so
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://mupdf.com/downloads/archive/mupdf-1.23.3-source.tar.gz
|
||||
sha256: 1ef9a6409bc0a3271586e1b16f78eb156a579521cd212a124b57c4da8b940aad
|
||||
- type: patch
|
||||
path: 01-mupdf-shared.patch
|
||||
|
||||
- name: CBconvert
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- cp cmd/cbconvert-gui/dist/linux/flatpak/modules.txt cmd/cbconvert-gui/vendor/
|
||||
- cd cmd/cbconvert-gui && $GOROOT/bin/go build -mod=vendor -trimpath -tags "extlib portal" -ldflags "-s -w -X main.appVersion=1.0.1"
|
||||
- install -Dm00755 cmd/cbconvert-gui/cbconvert-gui $FLATPAK_DEST/bin/cbconvert-gui
|
||||
- install -Dm00644 cmd/cbconvert-gui/dist/linux/cbconvert.png $FLATPAK_DEST/share/icons/hicolor/256x256/apps/$FLATPAK_ID.png
|
||||
- install -Dm00644 cmd/cbconvert-gui/dist/linux/flatpak/$FLATPAK_ID.desktop $FLATPAK_DEST/share/applications/$FLATPAK_ID.desktop
|
||||
- install -Dm00644 cmd/cbconvert-gui/dist/linux/flatpak/$FLATPAK_ID.metainfo.xml $FLATPAK_DEST/share/metainfo/$FLATPAK_ID.metainfo.xml
|
||||
sources:
|
||||
- type: archive
|
||||
url: https://github.com/gen2brain/cbconvert/archive/refs/tags/v1.0.1.tar.gz
|
||||
sha256: 2ba51b3ef9cddeed8a64da34ee2d7a6cb931e50fb6f9c944c13c59cbcc5f33e2
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/gen2brain/cbconvert/@v/v1.0.1.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/gen2brain/cbconvert
|
||||
sha256: 57e0052fb89a7361e44f844dca0177f31935776f75ff799a3cc233e2260ef055
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/chai2010/webp/@v/v1.1.1.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/chai2010/webp
|
||||
sha256: f42fe1697007ea12155a5d1342d61787e1f34ca396192e4995dc4f2b350552ac
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/davecgh/go-spew/@v/v1.1.1.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/davecgh/go-spew
|
||||
sha256: 6b44a843951f371b7010c754ecc3cabefe815d5ced1c5b9409fb2d697e8a890d
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/disintegration/imaging/@v/v1.6.2.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/disintegration/imaging
|
||||
sha256: 2934e7bace3c8c0b1b4a07144197e8720b9ffbe922600e3a3c764f77792ac7c4
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/dustin/go-humanize/@v/v1.0.1.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/dustin/go-humanize
|
||||
sha256: 319404ea84c8a4e2d3d83f30988b006e7dd04976de3e1a1a90484ad94679fa46
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/fvbommel/sortorder/@v/v1.1.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/fvbommel/sortorder
|
||||
sha256: a4dbc58d2f72212474a7b5e1894b11d6712b687f4cc66ca1f6e202a375d252f7
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/gen2brain/go-fitz/@v/v1.23.1.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/gen2brain/go-fitz
|
||||
sha256: 60fc98c3420d7f7347e7a454fdfd66bca960accc8ed0ac582f4b4d82b38513c8
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/gen2brain/go-unarr/@v/v0.1.7.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/gen2brain/go-unarr
|
||||
sha256: 6f3418edef461c2e1ce5ba77141d402b04d4cf86e89b27c5dc20c922ac63ca4e
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/gen2brain/iup-go/iup/@v/v0.0.0-20230906093706-8b037fe6a7bd.zip
|
||||
strip-components: 4
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/gen2brain/iup-go/iup
|
||||
sha256: a75dbec0ce228214533ee971771233f8b57653828af04bc012f72b624b76be5d
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/godbus/dbus/v5/@v/v5.1.0.zip
|
||||
strip-components: 4
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/godbus/dbus/v5
|
||||
sha256: 03dfa8e71089a6f477310d15c4d3a036d82d028532881b50fee254358e782ad9
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/google/uuid/@v/v1.3.1.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/google/uuid
|
||||
sha256: 9d9d6cfb28ce6dbe4b518c42c6bccd67bb531a106859808f36e82a5c3fb8c64d
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/k0kubun/go-ansi/@v/v0.0.0-20180517002512-3bf9e2903213.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/k0kubun/go-ansi
|
||||
sha256: 28366085f1787eaf9d6589f050455855f01caf559f2c6d1c4f4e590cd0abbdef
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/mattn/go-isatty/@v/v0.0.16.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/mattn/go-isatty
|
||||
sha256: 3d5ff19c4b2a2a164feb84f5cc38af349380c0c4a03d0443dce40bbd6ec3fd2b
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/mattn/go-runewidth/@v/v0.0.13.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/mattn/go-runewidth
|
||||
sha256: c104e14c1612a6d736bd109fe5fec9749a8146e1f7d37844d8a0a1296e00d4e9
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/mitchellh/colorstring/@v/v0.0.0-20190213212951-d06e56a500db.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/mitchellh/colorstring
|
||||
sha256: d0733284b20567055e374b420373f5508fa47e95204e59e4b8a66834e7e3964d
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/pmezard/go-difflib/@v/v1.0.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/pmezard/go-difflib
|
||||
sha256: de04cecc1a4b8d53e4357051026794bcbc54f2e6a260cfac508ce69d5d6457a0
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/rivo/uniseg/@v/v0.3.4.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/rivo/uniseg
|
||||
sha256: 410ba5034e1683946d68e7fd50ad793c2ad72b157f2959fe4719449666d1e63f
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/schollz/progressbar/v3/@v/v3.10.0.zip
|
||||
strip-components: 4
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/schollz/progressbar/v3
|
||||
sha256: 2cdbf5e4bc314bf140911a923f22d5d9401d73f3b2c7311f6df5c2837d2743ba
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/spf13/pflag/@v/v1.0.5.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/spf13/pflag
|
||||
sha256: fc6e704f2f6a84ddcdce6de0404e5340fa20c8676181bf5d381b17888107ba84
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/stretchr/objx/@v/v0.1.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/stretchr/objx
|
||||
sha256: 1fa10dab404ed7fc8ed2a033f8784187d5df3513ced3841ce39e46d37850eb1d
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/stretchr/testify/@v/v1.3.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/stretchr/testify
|
||||
sha256: 8c935aed71cc334d5bfdf04b34909d9965cf28f80198dec13fb954dc292e6588
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/strukturag/libheif/@v/v1.15.2.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/strukturag/libheif
|
||||
sha256: bec8ad8e81d33d96b9ea8d11036a14ff4501b62f2cb79dbe49147a701c5c1d38
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/github.com/yuin/goldmark/@v/v1.4.13.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/github.com/yuin/goldmark
|
||||
sha256: bb41a602b174345fda392c8ad83fcc93217c285c763699677630be90feb7a5e3
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/crypto/@v/v0.0.0-20210921155107-089bfa567519.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/crypto
|
||||
sha256: eb2426a7891915213cc5da1da7b6fc6e9e2cf253d518d8e169e038e287f414e3
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/image/@v/v0.12.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/image
|
||||
sha256: eee2ea2f688c5bbe2a8ce36ba57f37573f5b63828560cd34f8621f0f8d07e6bd
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/mod/@v/v0.8.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/mod
|
||||
sha256: 4ae8176799d8cda819e70731ba6855735003e7e4930436e34584c75c96c496e0
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/net/@v/v0.6.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/net
|
||||
sha256: 7ff2f50b1f3a58833f867d1646421569a91d8c19a0999793c5af79b10c16b8b8
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/sync/@v/v0.3.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/sync
|
||||
sha256: 1870e7a196f7119d4c6edba7de9cdfc49ee13c8cb7921f3a947568171c6152e0
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/sys/@v/v0.5.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/sys
|
||||
sha256: cf47336ac1bf675fa6d6dd5ac5399b0143c513404c449fa3f3380a58123c7908
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/term/@v/v0.5.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/term
|
||||
sha256: 7d89c49ab41306950128a0f4b7c67fb8e2d2f637ece8e024e6cf38d17a33193b
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/text/@v/v0.13.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/text
|
||||
sha256: ed544fb017e967c053892df7b068612fce707ba32b57f35824cb041e31c6ae0f
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/tools/@v/v0.6.0.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/tools
|
||||
sha256: 9a29c8904c2acd4b65825e916cbdaf417086f35bb68c54af9a6283a0e1341e85
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/golang.org/x/xerrors/@v/v0.0.0-20190717185122-a985d3407aa7.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/golang.org/x/xerrors
|
||||
sha256: c4e9f063cfed546c90f00a9657deac4f915a8994f8cbe6dbd3f18e79eb8302cf
|
||||
|
||||
- type: archive
|
||||
url: https://proxy.golang.org/gopkg.in/gographics/imagick.v3/@v/v3.4.3.zip
|
||||
strip-components: 3
|
||||
dest: cmd/cbconvert-gui/vendor/gopkg.in/gographics/imagick.v3
|
||||
sha256: ae0a425a2ffcbe92447c5da005deff2c0aac1e25995c443b86ae4bd48861d9e1
|
||||
@@ -1,45 +0,0 @@
|
||||
# github.com/chai2010/webp v1.1.1
|
||||
## explicit; go 1.17
|
||||
github.com/chai2010/webp
|
||||
# github.com/disintegration/imaging v1.6.2
|
||||
## explicit
|
||||
github.com/disintegration/imaging
|
||||
# github.com/dustin/go-humanize v1.0.1
|
||||
## explicit; go 1.16
|
||||
github.com/dustin/go-humanize
|
||||
# github.com/fvbommel/sortorder v1.1.0
|
||||
## explicit; go 1.13
|
||||
github.com/fvbommel/sortorder
|
||||
# github.com/gen2brain/cbconvert v1.0.0
|
||||
## explicit; go 1.21
|
||||
github.com/gen2brain/cbconvert
|
||||
# github.com/gen2brain/go-fitz v1.23.1
|
||||
## explicit; go 1.20
|
||||
github.com/gen2brain/go-fitz
|
||||
# github.com/gen2brain/go-unarr v0.1.7
|
||||
## explicit; go 1.18
|
||||
github.com/gen2brain/go-unarr
|
||||
github.com/gen2brain/go-unarr/unarrc
|
||||
# github.com/gen2brain/iup-go/iup v0.0.0-20230906093706-8b037fe6a7bd
|
||||
## explicit; go 1.19
|
||||
github.com/gen2brain/iup-go/iup
|
||||
github.com/gen2brain/iup-go/iup/manifest
|
||||
# github.com/godbus/dbus/v5 v5.1.0
|
||||
## explicit; go 1.12
|
||||
github.com/godbus/dbus/v5
|
||||
# github.com/google/uuid v1.3.1
|
||||
## explicit
|
||||
github.com/google/uuid
|
||||
# golang.org/x/image v0.12.0
|
||||
## explicit; go 1.12
|
||||
golang.org/x/image/bmp
|
||||
golang.org/x/image/ccitt
|
||||
golang.org/x/image/tiff
|
||||
golang.org/x/image/tiff/lzw
|
||||
# golang.org/x/sync v0.3.0
|
||||
## explicit; go 1.17
|
||||
golang.org/x/sync/errgroup
|
||||
# gopkg.in/gographics/imagick.v3 v3.4.3
|
||||
## explicit; go 1.13
|
||||
gopkg.in/gographics/imagick.v3/imagick
|
||||
gopkg.in/gographics/imagick.v3/imagick/types
|
||||
@@ -1,10 +1,10 @@
|
||||
[Desktop Entry]
|
||||
Name=CBconvert
|
||||
GenericName=A Comic Book converter
|
||||
Comment=A comic converter with support for .cb*, .pdf, .xps, .epub, .mobi and directories
|
||||
Comment=A comic converter with support for .cb*, .pdf, .xps, .epub, .mobi and directories.
|
||||
Exec=cbconvert-gui
|
||||
Icon=io.github.gen2brain.cbconvert
|
||||
Terminal=false
|
||||
Type=Application
|
||||
StartupNotify=true
|
||||
Categories=Graphics;
|
||||
Categories=Graphics;Utility;
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>io.github.gen2brain.cbconvert</id>
|
||||
|
||||
<name>CBconvert</name>
|
||||
<summary>A Comic Book converter</summary>
|
||||
|
||||
<metadata_license>MIT</metadata_license>
|
||||
<project_license>GPL-3</project_license>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
A comic book converter with support for .cb*, .pdf, .xps, .epub, .mobi and directories.
|
||||
Images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, JXL or 4-Bit BMP (16 colors) file format.
|
||||
</p>
|
||||
</description>
|
||||
|
||||
<developer id="com.github.gen2brain">
|
||||
<name>Milan Nikolic</name>
|
||||
</developer>
|
||||
|
||||
<launchable type="desktop-id">io.github.gen2brain.cbconvert.desktop</launchable>
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Options</caption>
|
||||
<image>https://raw.githubusercontent.com/gen2brain/cbconvert/master/cmd/cbconvert-gui/screenshots/linux-01.jpg</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<caption>Converting</caption>
|
||||
<image>https://raw.githubusercontent.com/gen2brain/cbconvert/master/cmd/cbconvert-gui/screenshots/linux-02.jpg</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<url type="homepage">https://github.com/gen2brain/cbconvert</url>
|
||||
<url type="bugtracker">https://github.com/gen2brain/cbconvert/issues</url>
|
||||
|
||||
<content_rating type="oars-1.1"/>
|
||||
|
||||
<releases>
|
||||
<release version="1.2.0" date="2026-06-27" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Support for RAR5</li>
|
||||
<li>Lossless compression for WebP, AVIF, and JXL</li>
|
||||
<li>Control over encoder effort and speed</li>
|
||||
<li>Combine multiple comics into a single file</li>
|
||||
<li>Save and switch between settings profiles</li>
|
||||
<li>Multi-language interface</li>
|
||||
<li>Choose the document rendering resolution (DPI)</li>
|
||||
<li>Option to skip upscaling of smaller images</li>
|
||||
<li>Refreshed interface with page-by-page preview and cover thumbnails in the file picker</li>
|
||||
<li>Various fixes and smaller improvements</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.2.0</url>
|
||||
</release>
|
||||
<release version="1.1.0" date="2024-11-06" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Use jpegli for smaller files</li>
|
||||
<li>Remove ImageMagick dependency</li>
|
||||
<li>Wayland support</li>
|
||||
<li>Support for DOCX and PPTX</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.1.0</url>
|
||||
</release>
|
||||
<release version="1.0.4" date="2024-02-08" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Update modules and dependencies</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.4</url>
|
||||
</release>
|
||||
<release version="1.0.3" date="2023-09-14" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add content rating</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.3</url>
|
||||
</release>
|
||||
<release version="1.0.2" date="2023-09-14" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Change flatpak exec/command name to cbconvert</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.2</url>
|
||||
</release>
|
||||
<release version="1.0.1" date="2023-09-14" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add flatpak manifest</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.1</url>
|
||||
</release>
|
||||
<release version="1.0.0" date="2023-09-14" type="stable">
|
||||
<description>
|
||||
<ul>
|
||||
<li>Add GUI</li>
|
||||
<li>Add support for JPEG XL</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="details">https://github.com/gen2brain/cbconvert/releases/tag/v1.0.0</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
</component>
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1,4 +1,4 @@
|
||||
[Thumbnailer Entry]
|
||||
TryExec=cbconvert
|
||||
Exec=cbconvert thumbnail --quiet --width %s --outfile %o %i
|
||||
MimeType=application/pdf;application/x-cb7;application/x-cbt;application/epub+zip;application/vnd.comicbook-rar;application/vnd.comicbook+zip;application/x-mobipocket-ebook;application/vnd.ms-xpsdocument;
|
||||
MimeType=application/pdf;application/x-cb7;application/x-cbt;application/epub+zip;application/vnd.comicbook-rar;application/vnd.comicbook+zip;application/x-mobipocket-ebook;application/vnd.ms-xpsdocument;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;
|
||||
@@ -0,0 +1,271 @@
|
||||
# github.com/STARRY-S/zip v0.2.3
|
||||
## explicit; go 1.23
|
||||
github.com/STARRY-S/zip
|
||||
# github.com/andybalholm/brotli v1.2.1
|
||||
## explicit; go 1.22
|
||||
github.com/andybalholm/brotli
|
||||
github.com/andybalholm/brotli/flate
|
||||
github.com/andybalholm/brotli/matchfinder
|
||||
# github.com/anthonynsimon/bild v0.15.0
|
||||
## explicit; go 1.26
|
||||
github.com/anthonynsimon/bild/adjust
|
||||
github.com/anthonynsimon/bild/clone
|
||||
github.com/anthonynsimon/bild/math/f64
|
||||
github.com/anthonynsimon/bild/parallel
|
||||
github.com/anthonynsimon/bild/transform
|
||||
github.com/anthonynsimon/bild/util
|
||||
# github.com/bodgit/plumbing v1.3.0
|
||||
## explicit; go 1.13
|
||||
github.com/bodgit/plumbing
|
||||
# github.com/bodgit/sevenzip v1.6.4
|
||||
## explicit; go 1.25.0
|
||||
github.com/bodgit/sevenzip
|
||||
github.com/bodgit/sevenzip/internal/aes7z
|
||||
github.com/bodgit/sevenzip/internal/bcj2
|
||||
github.com/bodgit/sevenzip/internal/bra
|
||||
github.com/bodgit/sevenzip/internal/brotli
|
||||
github.com/bodgit/sevenzip/internal/bzip2
|
||||
github.com/bodgit/sevenzip/internal/deflate
|
||||
github.com/bodgit/sevenzip/internal/delta
|
||||
github.com/bodgit/sevenzip/internal/lz4
|
||||
github.com/bodgit/sevenzip/internal/lzma
|
||||
github.com/bodgit/sevenzip/internal/lzma2
|
||||
github.com/bodgit/sevenzip/internal/pool
|
||||
github.com/bodgit/sevenzip/internal/ppmd
|
||||
github.com/bodgit/sevenzip/internal/util
|
||||
github.com/bodgit/sevenzip/internal/zstd
|
||||
# github.com/bodgit/windows v1.0.1
|
||||
## explicit; go 1.13
|
||||
github.com/bodgit/windows
|
||||
# github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707
|
||||
## explicit; go 1.9
|
||||
github.com/dsnet/compress
|
||||
github.com/dsnet/compress/bzip2
|
||||
github.com/dsnet/compress/bzip2/internal/sais
|
||||
github.com/dsnet/compress/internal
|
||||
github.com/dsnet/compress/internal/errors
|
||||
github.com/dsnet/compress/internal/prefix
|
||||
# github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d
|
||||
## explicit; go 1.13
|
||||
github.com/dsoprea/go-exif/v2
|
||||
github.com/dsoprea/go-exif/v2/common
|
||||
github.com/dsoprea/go-exif/v2/undefined
|
||||
# github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd
|
||||
## explicit; go 1.13
|
||||
github.com/dsoprea/go-logging
|
||||
# github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d
|
||||
## explicit; go 1.13
|
||||
github.com/dsoprea/go-png-image-structure
|
||||
# github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349
|
||||
## explicit; go 1.12
|
||||
github.com/dsoprea/go-utility/image
|
||||
# github.com/dustin/go-humanize v1.0.1
|
||||
## explicit; go 1.16
|
||||
github.com/dustin/go-humanize
|
||||
# github.com/ebitengine/purego v0.10.1
|
||||
## explicit; go 1.18
|
||||
github.com/ebitengine/purego
|
||||
github.com/ebitengine/purego/internal/cgo
|
||||
github.com/ebitengine/purego/internal/fakecgo
|
||||
github.com/ebitengine/purego/internal/strings
|
||||
github.com/ebitengine/purego/internal/xreflect
|
||||
# github.com/fvbommel/sortorder v1.1.0
|
||||
## explicit; go 1.13
|
||||
github.com/fvbommel/sortorder
|
||||
# github.com/gen2brain/avif v0.5.1
|
||||
## explicit; go 1.25.0
|
||||
github.com/gen2brain/avif
|
||||
# github.com/gen2brain/cbconvert v1.2.0
|
||||
## explicit; go 1.26
|
||||
github.com/gen2brain/cbconvert
|
||||
# github.com/gen2brain/go-fitz v1.28.1
|
||||
## explicit; go 1.24.0
|
||||
github.com/gen2brain/go-fitz
|
||||
github.com/gen2brain/go-fitz/include/mupdf
|
||||
github.com/gen2brain/go-fitz/include/mupdf/fitz
|
||||
github.com/gen2brain/go-fitz/libs
|
||||
# github.com/gen2brain/iup-go/iup v0.32.1-0.20260627135200-7df674d35173
|
||||
## explicit; go 1.21
|
||||
github.com/gen2brain/iup-go/iup
|
||||
github.com/gen2brain/iup-go/iup/external
|
||||
github.com/gen2brain/iup-go/iup/external/include
|
||||
github.com/gen2brain/iup-go/iup/external/src
|
||||
github.com/gen2brain/iup-go/iup/external/src/android
|
||||
github.com/gen2brain/iup-go/iup/external/src/cocoa
|
||||
github.com/gen2brain/iup-go/iup/external/src/cocoatouch
|
||||
github.com/gen2brain/iup-go/iup/external/src/efl
|
||||
github.com/gen2brain/iup-go/iup/external/src/gtk
|
||||
github.com/gen2brain/iup-go/iup/external/src/gtk4
|
||||
github.com/gen2brain/iup-go/iup/external/src/haiku
|
||||
github.com/gen2brain/iup-go/iup/external/src/mot
|
||||
github.com/gen2brain/iup-go/iup/external/src/qt
|
||||
github.com/gen2brain/iup-go/iup/external/src/win
|
||||
github.com/gen2brain/iup-go/iup/external/src/win/wdl
|
||||
github.com/gen2brain/iup-go/iup/external/src/win/wdl/dummy
|
||||
github.com/gen2brain/iup-go/iup/external/src/winui
|
||||
github.com/gen2brain/iup-go/iup/external/src/winui/winrt
|
||||
github.com/gen2brain/iup-go/iup/external/src/winui/winrt/impl
|
||||
github.com/gen2brain/iup-go/iup/external/srcctrl
|
||||
github.com/gen2brain/iup-go/iup/external/srcctrl/matrix
|
||||
github.com/gen2brain/iup-go/iup/external/srcctrl/matrixex
|
||||
github.com/gen2brain/iup-go/iup/external/srcgl
|
||||
github.com/gen2brain/iup-go/iup/external/srcplot
|
||||
github.com/gen2brain/iup-go/iup/external/srcweb
|
||||
github.com/gen2brain/iup-go/iup/manifest
|
||||
# github.com/gen2brain/jpegli v0.4.1
|
||||
## explicit; go 1.25.0
|
||||
github.com/gen2brain/jpegli
|
||||
# github.com/gen2brain/jpegn v0.4.2
|
||||
## explicit; go 1.23.0
|
||||
github.com/gen2brain/jpegn
|
||||
# github.com/gen2brain/jpegxl v0.5.2
|
||||
## explicit; go 1.25.0
|
||||
github.com/gen2brain/jpegxl
|
||||
# github.com/gen2brain/webp v0.6.1
|
||||
## explicit; go 1.23
|
||||
github.com/gen2brain/webp
|
||||
# github.com/go-errors/errors v1.5.1
|
||||
## explicit; go 1.14
|
||||
github.com/go-errors/errors
|
||||
# github.com/golang/geo v0.0.0-20260625163123-7c0e84413537
|
||||
## explicit; go 1.23.0
|
||||
github.com/golang/geo/r1
|
||||
github.com/golang/geo/r2
|
||||
github.com/golang/geo/r3
|
||||
github.com/golang/geo/s1
|
||||
github.com/golang/geo/s2
|
||||
# github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
## explicit; go 1.18
|
||||
github.com/hashicorp/golang-lru/v2
|
||||
github.com/hashicorp/golang-lru/v2/internal
|
||||
github.com/hashicorp/golang-lru/v2/simplelru
|
||||
# github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25
|
||||
## explicit
|
||||
github.com/jsummers/gobmp
|
||||
# github.com/klauspost/compress v1.18.6
|
||||
## explicit; go 1.24
|
||||
github.com/klauspost/compress
|
||||
github.com/klauspost/compress/flate
|
||||
github.com/klauspost/compress/fse
|
||||
github.com/klauspost/compress/gzip
|
||||
github.com/klauspost/compress/huff0
|
||||
github.com/klauspost/compress/internal/cpuinfo
|
||||
github.com/klauspost/compress/internal/godebug
|
||||
github.com/klauspost/compress/internal/le
|
||||
github.com/klauspost/compress/internal/race
|
||||
github.com/klauspost/compress/internal/snapref
|
||||
github.com/klauspost/compress/s2
|
||||
github.com/klauspost/compress/zip
|
||||
github.com/klauspost/compress/zlib
|
||||
github.com/klauspost/compress/zstd
|
||||
github.com/klauspost/compress/zstd/internal/xxhash
|
||||
# github.com/klauspost/pgzip v1.2.6
|
||||
## explicit
|
||||
github.com/klauspost/pgzip
|
||||
# github.com/mholt/archives v0.1.5
|
||||
## explicit; go 1.24.0
|
||||
github.com/mholt/archives
|
||||
# github.com/mikelolasagasti/xz v1.0.1
|
||||
## explicit; go 1.15
|
||||
github.com/mikelolasagasti/xz
|
||||
# github.com/minio/minlz v1.1.1
|
||||
## explicit; go 1.21
|
||||
github.com/minio/minlz
|
||||
github.com/minio/minlz/internal/race
|
||||
# github.com/nwaples/rardecode/v2 v2.2.5
|
||||
## explicit; go 1.21
|
||||
github.com/nwaples/rardecode/v2
|
||||
# github.com/pierrec/lz4/v4 v4.1.27
|
||||
## explicit; go 1.17
|
||||
github.com/pierrec/lz4/v4
|
||||
github.com/pierrec/lz4/v4/internal/lz4block
|
||||
github.com/pierrec/lz4/v4/internal/lz4errors
|
||||
github.com/pierrec/lz4/v4/internal/lz4stream
|
||||
github.com/pierrec/lz4/v4/internal/xxh32
|
||||
# github.com/sorairolake/lzip-go v0.3.8
|
||||
## explicit; go 1.22
|
||||
github.com/sorairolake/lzip-go
|
||||
# github.com/spf13/afero v1.15.0
|
||||
## explicit; go 1.23.0
|
||||
github.com/spf13/afero
|
||||
github.com/spf13/afero/internal/common
|
||||
github.com/spf13/afero/mem
|
||||
# github.com/stangelandcl/ppmd v0.1.1
|
||||
## explicit; go 1.17
|
||||
github.com/stangelandcl/ppmd
|
||||
github.com/stangelandcl/ppmd/internal/h7z
|
||||
# github.com/tetratelabs/wazero v1.12.0
|
||||
## explicit; go 1.25.0
|
||||
github.com/tetratelabs/wazero
|
||||
github.com/tetratelabs/wazero/api
|
||||
github.com/tetratelabs/wazero/experimental
|
||||
github.com/tetratelabs/wazero/experimental/sys
|
||||
github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1
|
||||
github.com/tetratelabs/wazero/internal/descriptor
|
||||
github.com/tetratelabs/wazero/internal/engine/interpreter
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/backend
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/backend/isa/amd64
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/backend/isa/arm64
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/backend/regalloc
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/frontend
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/ssa
|
||||
github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi
|
||||
github.com/tetratelabs/wazero/internal/expctxkeys
|
||||
github.com/tetratelabs/wazero/internal/filecache
|
||||
github.com/tetratelabs/wazero/internal/ieee754
|
||||
github.com/tetratelabs/wazero/internal/internalapi
|
||||
github.com/tetratelabs/wazero/internal/leb128
|
||||
github.com/tetratelabs/wazero/internal/moremath
|
||||
github.com/tetratelabs/wazero/internal/platform
|
||||
github.com/tetratelabs/wazero/internal/sock
|
||||
github.com/tetratelabs/wazero/internal/sys
|
||||
github.com/tetratelabs/wazero/internal/sysfs
|
||||
github.com/tetratelabs/wazero/internal/u32
|
||||
github.com/tetratelabs/wazero/internal/u64
|
||||
github.com/tetratelabs/wazero/internal/version
|
||||
github.com/tetratelabs/wazero/internal/wasip1
|
||||
github.com/tetratelabs/wazero/internal/wasm
|
||||
github.com/tetratelabs/wazero/internal/wasm/binary
|
||||
github.com/tetratelabs/wazero/internal/wasmdebug
|
||||
github.com/tetratelabs/wazero/internal/wasmruntime
|
||||
github.com/tetratelabs/wazero/sys
|
||||
# github.com/ulikunitz/xz v0.5.15
|
||||
## explicit; go 1.12
|
||||
github.com/ulikunitz/xz
|
||||
github.com/ulikunitz/xz/internal/hash
|
||||
github.com/ulikunitz/xz/internal/xlog
|
||||
github.com/ulikunitz/xz/lzma
|
||||
# go4.org v0.0.0-20260112195520-a5071408f32f
|
||||
## explicit; go 1.24.0
|
||||
go4.org/readerutil
|
||||
# golang.org/x/image v0.43.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/image/ccitt
|
||||
golang.org/x/image/internal/safemath
|
||||
golang.org/x/image/tiff
|
||||
golang.org/x/image/tiff/lzw
|
||||
# golang.org/x/net v0.56.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/net/context
|
||||
# golang.org/x/sync v0.21.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/sync/errgroup
|
||||
# golang.org/x/sys v0.46.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
# golang.org/x/text v0.38.0
|
||||
## explicit; go 1.25.0
|
||||
golang.org/x/text/encoding
|
||||
golang.org/x/text/encoding/internal
|
||||
golang.org/x/text/encoding/internal/identifier
|
||||
golang.org/x/text/encoding/unicode
|
||||
golang.org/x/text/internal/utf8internal
|
||||
golang.org/x/text/runes
|
||||
golang.org/x/text/transform
|
||||
golang.org/x/text/unicode/norm
|
||||
# gopkg.in/yaml.v2 v2.4.0
|
||||
## explicit; go 1.15
|
||||
gopkg.in/yaml.v2
|
||||
@@ -12,7 +12,7 @@
|
||||
<string>icon</string>
|
||||
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.github.gen2brain.cbconvert</string>
|
||||
<string>io.github.gen2brain.cbconvert</string>
|
||||
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>19H2</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>iup</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>br.puc-rio.tecgraf.iup</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>iup</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleSupportedPlatforms</key>
|
||||
<array>
|
||||
<string>MacOSX</string>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>DTCompiler</key>
|
||||
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>12D4e</string>
|
||||
<key>DTPlatformName</key>
|
||||
<string>macosx</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>11.1</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>20C63</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx11.1</string>
|
||||
<key>DTXcode</key>
|
||||
<string>1240</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>12D4e</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.14</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 Tecgraf, PUC-Rio, Brazil. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,22 +1,54 @@
|
||||
module github.com/gen2brain/cbconvert/cmd/cbconvert-gui
|
||||
|
||||
go 1.21
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/gen2brain/cbconvert v1.0.0
|
||||
github.com/gen2brain/iup-go/iup v0.0.0-20230906093706-8b037fe6a7bd
|
||||
github.com/godbus/dbus/v5 v5.1.0
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/gen2brain/cbconvert v1.2.0
|
||||
github.com/gen2brain/iup-go/iup v0.32.1-0.20260627135200-7df674d35173
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chai2010/webp v1.1.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
github.com/anthonynsimon/bild v0.15.0 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.4 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d // indirect
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/gen2brain/go-fitz v1.23.1 // indirect
|
||||
github.com/gen2brain/go-unarr v0.1.7 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
golang.org/x/image v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
gopkg.in/gographics/imagick.v3 v3.4.3 // indirect
|
||||
github.com/ebitengine/purego v0.10.1 // indirect
|
||||
github.com/gen2brain/avif v0.5.1 // indirect
|
||||
github.com/gen2brain/go-fitz v1.28.1 // indirect
|
||||
github.com/gen2brain/jpegli v0.4.1 // indirect
|
||||
github.com/gen2brain/jpegn v0.4.2 // indirect
|
||||
github.com/gen2brain/jpegxl v0.5.2 // indirect
|
||||
github.com/gen2brain/webp v0.6.1 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/mholt/archives v0.1.5 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.1.1 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/stangelandcl/ppmd v0.1.1 // indirect
|
||||
github.com/tetratelabs/wazero v1.12.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/image v0.43.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,58 +1,145 @@
|
||||
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/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
|
||||
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/anthonynsimon/bild v0.15.0 h1:FzvaNLuNlAPKw1Xz7V2WYOcGIEBMj8Y6ZyAk7CI+HzA=
|
||||
github.com/anthonynsimon/bild v0.15.0/go.mod h1:qIgJ9FldkCn0iy5Ad24fzUkz5R+iJ0WfhiV+6FeCB5A=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.4 h1:iHiVJfxbrB6RF4X+snI2MpVgNBKmVfGaTqZGNlMQIU0=
|
||||
github.com/bodgit/sevenzip v1.6.4/go.mod h1:ZtNi5KNgHXeXg1G7WiF0IWSuFE2eG6lt/cTGlvuirO0=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
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/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d h1:yeH8wrJa3+8uKKDAdURHUK1ds2UvKhMqX2MiOdVeKPs=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210512043655-120bcdb2a55e/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
|
||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d h1:8+qI8ant/vZkNSsbwSjIR6XJfWcDVTg/qx/3pRUUZNA=
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d/go.mod h1:yTR3tKgyk20phAFg6IE9ulMA5NjEDD2wyx+okRFLVtw=
|
||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4=
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
|
||||
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
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 v1.0.0 h1:6szCltgrC/Vq+XuknuGKjS6E1J5zRLmqCnHXnvALCvY=
|
||||
github.com/gen2brain/cbconvert v1.0.0/go.mod h1:3zoSNHGpunxvS5cXpge9XwU585Zxsr4ly13dryeQCGk=
|
||||
github.com/gen2brain/go-fitz v1.23.1 h1:x69/szWZXpI3jZ57mMqCg7WqqvtYnQG0lXts3L6M1Fc=
|
||||
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/go.mod h1:MK9a3hddpaIxjEtrE1f/LA5yJ7gA34cS7Oyr325sY9s=
|
||||
github.com/gen2brain/iup-go/iup v0.0.0-20230906093706-8b037fe6a7bd h1:k1EaRqSEu/2eahOBY44uYZevryE1HQBz0t7RYCufnHI=
|
||||
github.com/gen2brain/iup-go/iup v0.0.0-20230906093706-8b037fe6a7bd/go.mod h1:HeMeojpQFldBWCMmU5dkmU640AdEcS+SPyEVLMJjjrw=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/gen2brain/avif v0.5.1 h1:LQzLsJpWyGlsa4wuZ3D57qEbCiICIK7Yidz5ZPEwzTk=
|
||||
github.com/gen2brain/avif v0.5.1/go.mod h1:QgrYqdVE9y40PCfArK9VakcMIpYeDYpZmCSLkW6C1n8=
|
||||
github.com/gen2brain/cbconvert v1.2.0 h1:eUs5MPzft+Lo9ADMYYQ5AzydO54Pv0YYUTm2lPo3XDo=
|
||||
github.com/gen2brain/cbconvert v1.2.0/go.mod h1:/6QtSgf47bDBmtkidNoYwNMHaebsDhPTKnDMmyNUd9E=
|
||||
github.com/gen2brain/go-fitz v1.28.1 h1:ToEYb2vN4ByaL2VmRNGk92Sa1UAkCn8bsObpA3WkQ48=
|
||||
github.com/gen2brain/go-fitz v1.28.1/go.mod h1:pY2hqAjp9Zy7qfPI2gwbJMHBFAdZpVXOLrRxD82l3Bs=
|
||||
github.com/gen2brain/iup-go/iup v0.32.1-0.20260627135200-7df674d35173 h1:nBt0N1ixK8eg/7RXJIC4b0WDPxfENqyT+rH1/STZGj4=
|
||||
github.com/gen2brain/iup-go/iup v0.32.1-0.20260627135200-7df674d35173/go.mod h1:V4f7tHOJAeHtjQ+ju795QKv6DGdLEb4L5cmWB1sjSzU=
|
||||
github.com/gen2brain/jpegli v0.4.1 h1:qc11IQU0jTYFltroulT4MXmbu9YRftqHV0YBZ0Bqz5o=
|
||||
github.com/gen2brain/jpegli v0.4.1/go.mod h1:zJ++s4symmKCN1CLkrY0dGXTY3s0NWbd94Rz9KLdCzk=
|
||||
github.com/gen2brain/jpegn v0.4.2 h1:sxy2yolV1eNA02uYtnqBFm4EIC3ETnars98aG7Dc4LM=
|
||||
github.com/gen2brain/jpegn v0.4.2/go.mod h1:YvcVOmVPSAsefH6yn9HBW3uY0EHlZwCMoiJXoAWfgL0=
|
||||
github.com/gen2brain/jpegxl v0.5.2 h1:1ou9YRziU8PbpkfFJIyxrNjYM+WaMl2n9LloABxkKsU=
|
||||
github.com/gen2brain/jpegxl v0.5.2/go.mod h1:Wlc6lqx03RJfhiQRyHa2e+8VQwT4/qv7zSRsNv9T+yE=
|
||||
github.com/gen2brain/webp v0.6.1 h1:ei7Y1SWpQcdqz3YNDNyn4y2nQanxs9WLzwW5/2DKS64=
|
||||
github.com/gen2brain/webp v0.6.1/go.mod h1:iGWMaCSw7t3I/Cv9llzEKmpnR36S8lS8VL/ZVjxU0JE=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 h1:KeIaDS/+VEy/bhDYjG3Z78dOyLAU4HXcVxmd0WYHJTE=
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
|
||||
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/nwaples/rardecode/v2 v2.2.5 h1:L5doqgGfQwI7qADJMqnkrSB86rpPsqQDrHeO0HWa5JY=
|
||||
github.com/nwaples/rardecode/v2 v2.2.5/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
||||
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
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/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/stangelandcl/ppmd v0.1.1 h1:c25QazhlWUn5nmR1QOzafKhQxBicAr7GGCKER2aJ8H8=
|
||||
github.com/stangelandcl/ppmd v0.1.1/go.mod h1:Rrv7M+/2P5jYr/GMLhBl7Ug3uJ1bUiVzr5LbbaV6xgY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
|
||||
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
|
||||
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.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
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.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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
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.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-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.3 h1:9plKFE/Us913jBN6KohtLG9FNW8LPvfpjiGAORIiEHg=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.3/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -0,0 +1,513 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/fvbommel/sortorder"
|
||||
|
||||
"github.com/gen2brain/cbconvert"
|
||||
"github.com/gen2brain/cbconvert/cmd/cbconvert-gui/i18n"
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
// selectRow focuses and selects the given 0-based row in the table.
|
||||
func selectRow(i int) {
|
||||
if i < 0 || i >= len(files) {
|
||||
return
|
||||
}
|
||||
|
||||
index = i
|
||||
iup.GetHandle("Table").SetAttribute("FOCUSCELL", fmt.Sprintf("%d:1", i+1))
|
||||
}
|
||||
|
||||
// onSort re-syncs the files slice to the table's displayed order after a sort.
|
||||
func onSort(ih iup.Ihandle, col int) int {
|
||||
n := len(files)
|
||||
if n < 2 {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
rowKey := func(name, size string) string {
|
||||
return name + "\x00" + size
|
||||
}
|
||||
|
||||
buckets := make(map[string][]int, n)
|
||||
for i, f := range files {
|
||||
size := strconv.FormatFloat(float64(f.Stat.Size())/(1024*1024), 'f', 2, 64)
|
||||
k := rowKey(f.Name, size)
|
||||
buckets[k] = append(buckets[k], i)
|
||||
}
|
||||
|
||||
var selPath string
|
||||
if index >= 0 && index < len(files) {
|
||||
selPath = files[index].Path
|
||||
}
|
||||
|
||||
reordered := make([]cbconvert.File, 0, n)
|
||||
for lin := 1; lin <= n; lin++ {
|
||||
k := rowKey(iup.GetAttributeId2(ih, "", lin, 1), iup.GetAttributeId2(ih, "", lin, 3))
|
||||
idxs := buckets[k]
|
||||
if len(idxs) == 0 {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
reordered = append(reordered, files[idxs[0]])
|
||||
buckets[k] = idxs[1:]
|
||||
}
|
||||
|
||||
files = reordered
|
||||
|
||||
index = -1
|
||||
if selPath != "" {
|
||||
for i, f := range files {
|
||||
if f.Path == selPath {
|
||||
selectRow(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
// addFiles appends files, natural-sorts the list, and rebuilds the table.
|
||||
func addFiles(fs []cbconvert.File) {
|
||||
if len(fs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
wasEmpty := len(files) == 0
|
||||
|
||||
var selPath string
|
||||
if index >= 0 && index < len(files) {
|
||||
selPath = files[index].Path
|
||||
}
|
||||
|
||||
files = append(files, fs...)
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
return sortorder.NaturalLess(files[i].Name, files[j].Name)
|
||||
})
|
||||
|
||||
t := iup.GetHandle("Table")
|
||||
t.SetAttribute("NUMLIN", strconv.Itoa(len(files)))
|
||||
for i, f := range files {
|
||||
lin := i + 1
|
||||
iup.SetAttributeId2(t, "", lin, 1, f.Name)
|
||||
iup.SetAttributeId2(t, "", lin, 2, cbconvert.FileType(f.Path))
|
||||
iup.SetAttributeId2(t, "", lin, 3, strconv.FormatFloat(float64(f.Stat.Size())/(1024*1024), 'f', 2, 64))
|
||||
}
|
||||
|
||||
if wasEmpty {
|
||||
selectRow(0)
|
||||
setActive()
|
||||
previewPost()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
index = -1
|
||||
for i, f := range files {
|
||||
if f.Path == selPath {
|
||||
selectRow(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
setActive()
|
||||
}
|
||||
|
||||
func previewPost() {
|
||||
if index == -1 || len(files) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
file := files[index]
|
||||
|
||||
// On a new file, fetch the count first; the Page POSTMESSAGE handler clamps previewPage and renders.
|
||||
if file.Path != previewPath {
|
||||
previewPath = file.Path
|
||||
iup.GetHandle("Loading").SetAttributes("VISIBLE=YES, START=YES")
|
||||
go pageCountPost(file)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
previewRender()
|
||||
}
|
||||
|
||||
// previewRenderSize is the size the cover is rendered at.
|
||||
const previewRenderSize = 1200
|
||||
|
||||
// previewRender renders the current file at previewPage off the UI thread.
|
||||
func previewRender() {
|
||||
if index == -1 || len(files) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
file := files[index]
|
||||
|
||||
iup.GetHandle("Loading").SetAttributes("VISIBLE=YES, START=YES")
|
||||
|
||||
opts := options()
|
||||
page := previewPage
|
||||
|
||||
go func(opts cbconvert.Options) {
|
||||
conv := cbconvert.New(opts)
|
||||
|
||||
var s string
|
||||
|
||||
img, err := conv.PreviewPage(file.Path, file.Stat, page, previewRenderSize, previewRenderSize)
|
||||
if err != nil {
|
||||
s = err.Error()
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
iup.PostMessage(iup.GetHandle("Preview"), s, page, img)
|
||||
}(opts)
|
||||
}
|
||||
|
||||
// pageCountPost computes the file's page count off the UI thread and posts it, tagged with the path, to the Page spin.
|
||||
func pageCountPost(file cbconvert.File) {
|
||||
n, err := cbconvert.New(cbconvert.NewOptions()).PageCount(file.Path, file.Stat)
|
||||
if err != nil || n < 1 {
|
||||
n = 1
|
||||
}
|
||||
|
||||
iup.PostMessage(iup.GetHandle("Page"), file.Path, n, nil)
|
||||
}
|
||||
|
||||
// onPageChanged re-renders the preview for the spin's page; dedupes so spin and typing don't both fire.
|
||||
func onPageChanged() int {
|
||||
page := iup.GetHandle("Page").GetInt("VALUE") - 1
|
||||
if page < 0 {
|
||||
page = 0
|
||||
}
|
||||
if page == previewPage {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
previewPage = page
|
||||
previewRender()
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onAddFiles(ih iup.Ihandle) int {
|
||||
args, err := fileDlg(i18n.Lng(i18n.DlgAddFiles), true, false, inputDirKey)
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
conv := cbconvert.New(options())
|
||||
|
||||
fs, err := conv.Files(args)
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
addFiles(fs)
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onAddDir(ih iup.Ihandle) int {
|
||||
args, err := fileDlg(i18n.Lng(i18n.DlgAddDir), false, true, inputDirKey)
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
conv := cbconvert.New(options())
|
||||
|
||||
fs, err := conv.Files(args)
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
addFiles(fs)
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onRemove(ih iup.Ihandle) int {
|
||||
if index < 0 || index >= len(files) {
|
||||
return iup.IGNORE
|
||||
}
|
||||
|
||||
iup.GetHandle("Table").SetAttribute("DELLIN", strconv.Itoa(index+1))
|
||||
files = slices.Delete(files, index, index+1)
|
||||
|
||||
if index >= len(files) {
|
||||
index = len(files) - 1
|
||||
}
|
||||
|
||||
setActive()
|
||||
if len(files) == 0 {
|
||||
clearPreview()
|
||||
} else {
|
||||
previewPost()
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
// clearPreview resets the preview state and repaints the canvas to its empty state.
|
||||
func clearPreview() {
|
||||
iup.Destroy(iup.GetHandle("cover"))
|
||||
hasCover = false
|
||||
previewPath = ""
|
||||
previewPage = 0
|
||||
|
||||
iup.GetHandle("PreviewInfo").SetAttribute("TITLE", "")
|
||||
iup.Update(iup.GetHandle("Preview"))
|
||||
}
|
||||
|
||||
func onRemoveAll(ih iup.Ihandle) int {
|
||||
index = -1
|
||||
files = make([]cbconvert.File, 0)
|
||||
|
||||
iup.GetHandle("Table").SetAttribute("NUMLIN", "0")
|
||||
clearPreview()
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onThumbnail(ih iup.Ihandle) int {
|
||||
conv := cbconvert.New(options())
|
||||
conv.Nfiles = len(files)
|
||||
activeConv = conv
|
||||
setBusy(true)
|
||||
|
||||
conv.OnProgress = func() {
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "progress2", 0, conv)
|
||||
}
|
||||
|
||||
var canceled = false
|
||||
conv.OnCancel = func() {
|
||||
canceled = true
|
||||
}
|
||||
|
||||
iup.GetHandle("dlg").SetCallback("K_ANY", iup.KAnyFunc(func(ih iup.Ihandle, c int) int {
|
||||
if c == iup.K_ESC {
|
||||
conv.Cancel()
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}))
|
||||
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "start", 0, conv)
|
||||
|
||||
go func(c *cbconvert.Converter) {
|
||||
for _, file := range files {
|
||||
if canceled {
|
||||
break
|
||||
}
|
||||
|
||||
if err := c.Thumbnail(file); err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "finish", 0, 0)
|
||||
}(conv)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onCover(ih iup.Ihandle) int {
|
||||
conv := cbconvert.New(options())
|
||||
conv.Nfiles = len(files)
|
||||
activeConv = conv
|
||||
setBusy(true)
|
||||
|
||||
conv.OnProgress = func() {
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "progress2", 0, conv)
|
||||
}
|
||||
|
||||
var canceled = false
|
||||
conv.OnCancel = func() {
|
||||
canceled = true
|
||||
}
|
||||
|
||||
iup.GetHandle("dlg").SetCallback("K_ANY", iup.KAnyFunc(func(ih iup.Ihandle, c int) int {
|
||||
if c == iup.K_ESC {
|
||||
conv.Cancel()
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}))
|
||||
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "start", 0, conv)
|
||||
|
||||
go func(c *cbconvert.Converter) {
|
||||
for _, file := range files {
|
||||
if canceled {
|
||||
break
|
||||
}
|
||||
|
||||
if err := c.Cover(file); err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "finish", 0, 0)
|
||||
}(conv)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onConvert(ih iup.Ihandle) int {
|
||||
if busy {
|
||||
if activeConv != nil {
|
||||
activeConv.Cancel()
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
conv := cbconvert.New(options())
|
||||
conv.Nfiles = len(files)
|
||||
activeConv = conv
|
||||
setBusy(true)
|
||||
|
||||
conv.OnStart = func() {
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "convert", 0, conv)
|
||||
}
|
||||
|
||||
conv.OnProgress = func() {
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "progress", 0, conv)
|
||||
}
|
||||
|
||||
iup.GetHandle("dlg").SetCallback("K_ANY", iup.KAnyFunc(func(ih iup.Ihandle, c int) int {
|
||||
if c == iup.K_ESC {
|
||||
conv.Cancel()
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
})).SetCallback("CLOSE_CB", iup.CloseFunc(func(ih iup.Ihandle) int {
|
||||
if err := os.RemoveAll(conv.Workdir); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}))
|
||||
|
||||
convertErr := func(err error) {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
if err := os.RemoveAll(conv.Workdir); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
if err := os.RemoveAll(conv.Workdir); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
go func(c *cbconvert.Converter) {
|
||||
if c.Opts.Combine {
|
||||
if err := c.Combine(files); err != nil {
|
||||
convertErr(err)
|
||||
}
|
||||
} else {
|
||||
for _, file := range files {
|
||||
if err := c.Convert(file); err != nil {
|
||||
convertErr(err)
|
||||
if errors.Is(err, context.Canceled) {
|
||||
break
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iup.PostMessage(iup.GetHandle("ProgressBar"), "finish", 0, 0)
|
||||
}(conv)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onOutputDirectory(ih iup.Ihandle) int {
|
||||
args, err := fileDlg(i18n.Lng(i18n.DlgOutputDir), false, true, outputDirKey)
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
iup.GetHandle("OutDir").SetAttribute("VALUE", args[0])
|
||||
}
|
||||
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onOutputFile(ih iup.Ihandle) int {
|
||||
name := saveDlg(i18n.Lng(i18n.DlgOutputFile), outputDirKey)
|
||||
if name != "" {
|
||||
iup.GetHandle("OutFile").SetAttribute("VALUE", filepath.Base(name))
|
||||
iup.GetHandle("OutDir").SetAttribute("VALUE", filepath.Dir(name))
|
||||
setActive()
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onFilterChanged(ih iup.Ihandle) int {
|
||||
switch ih.GetInt("VALUE") {
|
||||
case 1:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterNearest))
|
||||
case 2:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterBox))
|
||||
case 3:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterLinear))
|
||||
case 4:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterMitchell))
|
||||
case 5:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterCatmull))
|
||||
case 6:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterGaussian))
|
||||
case 7:
|
||||
ih.SetAttribute("TIP", i18n.Lng(i18n.FilterLanczos))
|
||||
}
|
||||
|
||||
previewPost()
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build !windows
|
||||
|
||||
package i18n
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// systemLangCode returns the two-letter language code from the POSIX locale environment, or "".
|
||||
func systemLangCode() string {
|
||||
for _, env := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} {
|
||||
v := os.Getenv(env)
|
||||
if v == "" || v == "C" || v == "POSIX" {
|
||||
continue
|
||||
}
|
||||
|
||||
if i := strings.IndexAny(v, "_.@"); i >= 0 {
|
||||
v = v[:i]
|
||||
}
|
||||
|
||||
return strings.ToLower(v)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
//go:build windows
|
||||
|
||||
package i18n
|
||||
|
||||
import "syscall"
|
||||
|
||||
// primaryLang maps a Windows primary-language id to a two-letter language code.
|
||||
var primaryLang = map[uint16]string{
|
||||
0x09: "en",
|
||||
0x16: "pt",
|
||||
0x0a: "es",
|
||||
0x05: "cs",
|
||||
0x19: "ru",
|
||||
0x07: "de",
|
||||
0x0c: "fr",
|
||||
0x04: "zh",
|
||||
0x11: "ja",
|
||||
0x10: "it",
|
||||
}
|
||||
|
||||
// systemLangCode returns the two-letter code of the user's default UI language, or "".
|
||||
func systemLangCode() string {
|
||||
r, _, _ := syscall.NewLazyDLL("kernel32.dll").NewProc("GetUserDefaultUILanguage").Call()
|
||||
|
||||
return primaryLang[uint16(r)&0x3ff]
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// Package i18n holds the cbconvert GUI message keys and per-language string packs.
|
||||
package i18n
|
||||
|
||||
import "github.com/gen2brain/iup-go/iup"
|
||||
|
||||
const (
|
||||
ColTitle = "COL_TITLE"
|
||||
ColType = "COL_TYPE"
|
||||
ColSize = "COL_SIZE"
|
||||
|
||||
TabInput = "TAB_INPUT"
|
||||
TabOutput = "TAB_OUTPUT"
|
||||
TabImage = "TAB_IMAGE"
|
||||
TabTransform = "TAB_TRANSFORM"
|
||||
|
||||
LblPage = "LBL_PAGE"
|
||||
TipPage = "TIP_PAGE"
|
||||
|
||||
TglRecursive = "TGL_RECURSIVE"
|
||||
TipRecursive = "TIP_RECURSIVE"
|
||||
TglNoRGB = "TGL_NORGB"
|
||||
TipNoRGB = "TIP_NORGB"
|
||||
TglNoCover = "TGL_NOCOVER"
|
||||
TipNoCover = "TIP_NOCOVER"
|
||||
TglNoNonImage = "TGL_NONONIMAGE"
|
||||
TipNoNonImage = "TIP_NONONIMAGE"
|
||||
TglNoConvert = "TGL_NOCONVERT"
|
||||
TipNoConvert = "TIP_NOCONVERT"
|
||||
LblMinSize = "LBL_MINSIZE"
|
||||
TipSize = "TIP_SIZE"
|
||||
LblDPI = "LBL_DPI"
|
||||
TipDPI = "TIP_DPI"
|
||||
|
||||
LblOutDir = "LBL_OUTDIR"
|
||||
TipOutDir = "TIP_OUTDIR"
|
||||
BtnBrowse = "BTN_BROWSE"
|
||||
LblSuffix = "LBL_SUFFIX"
|
||||
TipSuffix = "TIP_SUFFIX"
|
||||
LblArchive = "LBL_ARCHIVE"
|
||||
TipArchive = "TIP_ARCHIVE"
|
||||
LblCompression = "LBL_COMPRESSION"
|
||||
TipZipLevel = "TIP_ZIPLEVEL"
|
||||
TglCombine = "TGL_COMBINE"
|
||||
TipCombine = "TIP_COMBINE"
|
||||
LblOutFile = "LBL_OUTFILE"
|
||||
TipOutFile = "TIP_OUTFILE"
|
||||
|
||||
LblFormat = "LBL_FORMAT"
|
||||
TipFormat = "TIP_FORMAT"
|
||||
LblSize = "LBL_SIZE"
|
||||
CueWidth = "CUE_WIDTH"
|
||||
CueHeight = "CUE_HEIGHT"
|
||||
TipWidthHeight = "TIP_WIDTHHEIGHT"
|
||||
TglFit = "TGL_FIT"
|
||||
TipFit = "TIP_FIT"
|
||||
TglNoUpscale = "TGL_NOUPSCALE"
|
||||
TipNoUpscale = "TIP_NOUPSCALE"
|
||||
LblFilter = "LBL_FILTER"
|
||||
LblQuality = "LBL_QUALITY"
|
||||
TipQuality = "TIP_QUALITY"
|
||||
LblEffort = "LBL_EFFORT"
|
||||
TipEffort = "TIP_EFFORT"
|
||||
TglLossless = "TGL_LOSSLESS"
|
||||
TipLossless = "TIP_LOSSLESS"
|
||||
TglGrayscale = "TGL_GRAYSCALE"
|
||||
TipGrayscale = "TIP_GRAYSCALE"
|
||||
|
||||
LblBrightness = "LBL_BRIGHTNESS"
|
||||
TipBrightness = "TIP_BRIGHTNESS"
|
||||
LblContrast = "LBL_CONTRAST"
|
||||
TipContrast = "TIP_CONTRAST"
|
||||
LblRotate = "LBL_ROTATE"
|
||||
TipRotate = "TIP_ROTATE"
|
||||
|
||||
EffortMethod = "EFFORT_METHOD"
|
||||
EffortSpeed = "EFFORT_SPEED"
|
||||
EffortEffort = "EFFORT_EFFORT"
|
||||
TipEffortWebp = "TIP_EFFORT_WEBP"
|
||||
TipEffortAvif = "TIP_EFFORT_AVIF"
|
||||
TipEffortJxl = "TIP_EFFORT_JXL"
|
||||
|
||||
BtnAddFiles = "BTN_ADDFILES"
|
||||
BtnAddDir = "BTN_ADDDIR"
|
||||
BtnRemove = "BTN_REMOVE"
|
||||
BtnRemoveAll = "BTN_REMOVEALL"
|
||||
BtnThumbnail = "BTN_THUMBNAIL"
|
||||
BtnCover = "BTN_COVER"
|
||||
BtnConvert = "BTN_CONVERT"
|
||||
BtnCancel = "BTN_CANCEL"
|
||||
TipCancel = "TIP_CANCEL"
|
||||
BtnReset = "BTN_RESET"
|
||||
TipReset = "TIP_RESET"
|
||||
BtnSave = "BTN_SAVE"
|
||||
TipSave = "TIP_SAVE"
|
||||
BtnCommand = "BTN_COMMAND"
|
||||
TipCommand = "TIP_COMMAND"
|
||||
LblProfile = "LBL_PROFILE"
|
||||
TipProfile = "TIP_PROFILE"
|
||||
|
||||
TipThumbnail = "TIP_THUMBNAIL"
|
||||
TipCover = "TIP_COVER"
|
||||
TipConvert = "TIP_CONVERT"
|
||||
|
||||
StatusNeedFilesAndDir = "STATUS_NEED_FILES_AND_DIR"
|
||||
StatusNeedFiles = "STATUS_NEED_FILES"
|
||||
StatusNeedOutDir = "STATUS_NEED_OUTDIR"
|
||||
StatusFileOf = "STATUS_FILE_OF"
|
||||
|
||||
FilterNearest = "FILTER_NEAREST"
|
||||
FilterBox = "FILTER_BOX"
|
||||
FilterLinear = "FILTER_LINEAR"
|
||||
FilterMitchell = "FILTER_MITCHELL"
|
||||
FilterCatmull = "FILTER_CATMULL"
|
||||
FilterGaussian = "FILTER_GAUSSIAN"
|
||||
FilterLanczos = "FILTER_LANCZOS"
|
||||
|
||||
DlgAddFiles = "DLG_ADDFILES"
|
||||
DlgAddDir = "DLG_ADDDIR"
|
||||
DlgOutputDir = "DLG_OUTPUTDIR"
|
||||
DlgOutputFile = "DLG_OUTPUTFILE"
|
||||
DlgCommandLine = "DLG_COMMANDLINE"
|
||||
DlgSaveProfile = "DLG_SAVEPROFILE"
|
||||
|
||||
ParamName = "PARAM_NAME"
|
||||
MsgInvalidNameTitle = "MSG_INVALIDNAME_TITLE"
|
||||
MsgInvalidNameBody = "MSG_INVALIDNAME_BODY"
|
||||
|
||||
NoPreview = "NO_PREVIEW"
|
||||
)
|
||||
|
||||
// packs maps an IUP language name to its message pack, filled by each language file's init.
|
||||
var packs = map[string]map[string]string{}
|
||||
|
||||
// register adds a language pack; called from each i18n_<lang>.go init.
|
||||
func register(lang string, pack map[string]string) {
|
||||
packs[lang] = pack
|
||||
}
|
||||
|
||||
// langByCode maps a two-letter language code to the IUP language name.
|
||||
var langByCode = map[string]string{
|
||||
"en": "ENGLISH",
|
||||
"pt": "PORTUGUESE",
|
||||
"es": "SPANISH",
|
||||
"cs": "CZECH",
|
||||
"ru": "RUSSIAN",
|
||||
"de": "GERMAN",
|
||||
"fr": "FRENCH",
|
||||
"zh": "CHINESE",
|
||||
"ja": "JAPANESE",
|
||||
"it": "ITALIAN",
|
||||
}
|
||||
|
||||
// Lng wraps a message key for IUP's automatic language-string lookup.
|
||||
func Lng(key string) string {
|
||||
return "_@" + key
|
||||
}
|
||||
|
||||
// Str returns the translated string for a key, for use where IUP's "_@" prefix does not apply.
|
||||
func Str(key string) string {
|
||||
return iup.GetLanguageString(key)
|
||||
}
|
||||
|
||||
// Init detects the system language, switches IUP's predefined strings to it, and registers the message packs.
|
||||
func Init() {
|
||||
lang := detect()
|
||||
iup.SetLanguage(lang)
|
||||
|
||||
registerPack(packs["ENGLISH"])
|
||||
if lang != "ENGLISH" {
|
||||
if pack, ok := packs[lang]; ok {
|
||||
registerPack(pack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func registerPack(pack map[string]string) {
|
||||
for name, value := range pack {
|
||||
iup.SetLanguageString(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
// detect returns the IUP language name for the system locale, or ENGLISH.
|
||||
func detect() string {
|
||||
if name, ok := langByCode[systemLangCode()]; ok {
|
||||
return name
|
||||
}
|
||||
|
||||
return "ENGLISH"
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("CZECH", map[string]string{
|
||||
ColTitle: "Název",
|
||||
ColType: "Typ",
|
||||
ColSize: "Velikost (MiB)",
|
||||
|
||||
TabInput: "Vstup",
|
||||
TabOutput: "Výstup",
|
||||
TabImage: "Obrázek",
|
||||
TabTransform: "Transformace",
|
||||
|
||||
LblPage: "Stránka:",
|
||||
TipPage: "Náhled jiné stránky vybraného komiksu",
|
||||
|
||||
TglRecursive: " Procházet podadresáře",
|
||||
TipRecursive: "Zpracovat podadresáře rekurzivně",
|
||||
TglNoRGB: " Pouze obrázky ve stupních šedi",
|
||||
TipNoRGB: "Nepřevádět obrázky s barevným prostorem RGB",
|
||||
TglNoCover: " Vyloučit obálku",
|
||||
TipNoCover: "Nepřevádět obrázek obálky",
|
||||
TglNoNonImage: " Odstranit z archivu soubory, které nejsou obrázky",
|
||||
TipNoNonImage: "Odstranit z archivu soubory .nfo, .xml, .txt",
|
||||
TglNoConvert: " Netransformovat ani nepřevádět obrázky",
|
||||
TipNoConvert: "Kopírovat obrázky z archivu nebo adresáře beze změn",
|
||||
LblMinSize: "Minimální velikost (MiB):",
|
||||
TipSize: "Zpracovat pouze soubory větší než minimální velikost",
|
||||
LblDPI: "DPI dokumentu:",
|
||||
TipDPI: "Rozlišení pro vykreslování dokumentů (PDF, EPUB atd.); výchozí používá původní rozlišení",
|
||||
|
||||
LblOutDir: "Výstupní adresář:",
|
||||
TipOutDir: "Adresář, do kterého se zapisují převedené soubory (povinné)",
|
||||
BtnBrowse: "Procházet...",
|
||||
LblSuffix: "Přidat příponu k výstupnímu souboru:",
|
||||
TipSuffix: "Přidat příponu k názvu souboru, např. nazevsouboru_pripona.cbz",
|
||||
LblArchive: "Formát archivu:",
|
||||
TipArchive: "Výstupní kontejner: ZIP (.cbz) nebo nekomprimovaný TAR (.cbt)",
|
||||
LblCompression: "Komprese:",
|
||||
TipZipLevel: "Komprese ZIP: Uložit ji vypne, 1 je nejrychlejší, 9 nejmenší",
|
||||
TglCombine: " Sloučit do jednoho souboru",
|
||||
TipCombine: "Sloučit všechny uvedené soubory do jednoho archivu",
|
||||
LblOutFile: "Výstupní soubor:",
|
||||
TipOutFile: "Název sloučeného souboru (výchozí: první vstup + -combined)",
|
||||
|
||||
LblFormat: "Formát:",
|
||||
TipFormat: "Výstupní formát obrázku pro převedené stránky",
|
||||
LblSize: "Velikost:",
|
||||
CueWidth: "šířka",
|
||||
CueHeight: "výška",
|
||||
TipWidthHeight: "Pokud není nastavena šířka nebo výška, zachová se poměr stran obrázku",
|
||||
TglFit: " Nejlepší přizpůsobení",
|
||||
TipFit: "Nejlepší přizpůsobení požadované šířce a výšce",
|
||||
TglNoUpscale: " Nezvětšovat",
|
||||
TipNoUpscale: "Nezvětšovat obrázky, které jsou již menší než požadovaná velikost",
|
||||
LblFilter: "Filtr změny velikosti:",
|
||||
LblQuality: "Kvalita: ",
|
||||
TipQuality: "Kvalita ovlivňuje JPEG, WEBP, AVIF a JXL",
|
||||
LblEffort: "Úsilí:",
|
||||
TipEffort: "Rychlost/úsilí kodéru (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Bezztrátový",
|
||||
TipLossless: "Bezztrátová komprese (WEBP, AVIF, JXL), ignoruje kvalitu",
|
||||
TglGrayscale: " Stupně šedi",
|
||||
TipGrayscale: "Převést obrázky do stupňů šedi (monochromaticky)",
|
||||
|
||||
LblBrightness: "Jas: ",
|
||||
TipBrightness: "Upravit jas obrázků",
|
||||
LblContrast: "Kontrast: ",
|
||||
TipContrast: "Upravit kontrast obrázků",
|
||||
LblRotate: "Otočit:",
|
||||
TipRotate: "Otočit každou stránku po směru hodinových ručiček o zadaný úhel ve stupních",
|
||||
|
||||
EffortMethod: "Metoda",
|
||||
EffortSpeed: "Rychlost",
|
||||
EffortEffort: "Úsilí",
|
||||
TipEffortWebp: "Metoda WEBP, vyšší je lepší/pomalejší (0-6, výchozí 4)",
|
||||
TipEffortAvif: "Rychlost AVIF, vyšší je rychlejší/horší (0-10, výchozí 10)",
|
||||
TipEffortJxl: "Úsilí JXL, vyšší je lepší/pomalejší (1-10, výchozí 7)",
|
||||
|
||||
BtnAddFiles: "Přidat &soubory...",
|
||||
BtnAddDir: "Přidat &adresář...",
|
||||
BtnRemove: "Odebrat",
|
||||
BtnRemoveAll: "Odebrat vše",
|
||||
BtnThumbnail: "Náhled",
|
||||
BtnCover: "Obálka",
|
||||
BtnConvert: "&Převést",
|
||||
BtnCancel: "Zrušit",
|
||||
TipCancel: "Zrušit probíhající operaci (nebo stisknout Esc)",
|
||||
BtnReset: "Obnovit",
|
||||
TipReset: "Obnovit všechna nastavení na výchozí hodnoty",
|
||||
BtnSave: "Uložit",
|
||||
TipSave: "Uložit aktuální nastavení do profilu",
|
||||
BtnCommand: "Příkaz",
|
||||
TipCommand: "Zobrazit odpovídající příkazový řádek",
|
||||
LblProfile: "Profil:",
|
||||
TipProfile: "Vybrat profil nastavení",
|
||||
|
||||
TipThumbnail: "Extrahovat náhledy obálek",
|
||||
TipCover: "Extrahovat obálky",
|
||||
TipConvert: "Převést soubory do vybraného formátu",
|
||||
|
||||
StatusNeedFilesAndDir: "Přidejte soubory a nastavte výstupní adresář",
|
||||
StatusNeedFiles: "Přidejte soubory",
|
||||
StatusNeedOutDir: "Nastavte výstupní adresář",
|
||||
StatusFileOf: "Soubor %d z %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor je nejrychlejší převzorkovací filtr, bez vyhlazování",
|
||||
FilterBox: "Filtr Box (průměrování pixelů)",
|
||||
FilterLinear: "Linear je bilineární filtr, hladký a přiměřeně rychlý",
|
||||
FilterMitchell: "MitchellNetravali je hladký bikubický filtr",
|
||||
FilterCatmull: "CatmullRom je ostrý bikubický filtr",
|
||||
FilterGaussian: "Gaussian je rozostřovací filtr využívající Gaussovu funkci, užitečný pro odstranění šumu",
|
||||
FilterLanczos: "Lanczos je vysoce kvalitní převzorkovací filtr, pomalejší než bikubické filtry",
|
||||
|
||||
DlgAddFiles: "Přidat soubory",
|
||||
DlgAddDir: "Přidat adresář",
|
||||
DlgOutputDir: "Výstupní adresář",
|
||||
DlgOutputFile: "Výstupní soubor",
|
||||
DlgCommandLine: "Příkazový řádek",
|
||||
DlgSaveProfile: "Uložit profil",
|
||||
|
||||
ParamName: "Název: %s\n",
|
||||
MsgInvalidNameTitle: "Neplatný název",
|
||||
MsgInvalidNameBody: "Název profilu nesmí být prázdný ani obsahovat '.' nebo ';'.",
|
||||
|
||||
NoPreview: "Žádný náhled",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("GERMAN", map[string]string{
|
||||
ColTitle: "Titel",
|
||||
ColType: "Typ",
|
||||
ColSize: "Größe (MiB)",
|
||||
|
||||
TabInput: "Eingabe",
|
||||
TabOutput: "Ausgabe",
|
||||
TabImage: "Bild",
|
||||
TabTransform: "Transformation",
|
||||
|
||||
LblPage: "Seite:",
|
||||
TipPage: "Eine andere Seite des ausgewählten Comics als Vorschau anzeigen",
|
||||
|
||||
TglRecursive: " Unterverzeichnisse rekursiv",
|
||||
TipRecursive: "Unterverzeichnisse rekursiv verarbeiten",
|
||||
TglNoRGB: " Nur Graustufenbilder",
|
||||
TipNoRGB: "Bilder mit RGB-Farbraum nicht konvertieren",
|
||||
TglNoCover: " Titelbild ausschließen",
|
||||
TipNoCover: "Das Titelbild nicht konvertieren",
|
||||
TglNoNonImage: " Nicht-Bilddateien aus dem Archiv entfernen",
|
||||
TipNoNonImage: ".nfo-, .xml-, .txt-Dateien aus dem Archiv entfernen",
|
||||
TglNoConvert: " Bilder nicht transformieren oder konvertieren",
|
||||
TipNoConvert: "Bilder aus Archiv oder Verzeichnis ohne Änderungen kopieren",
|
||||
LblMinSize: "Mindestgröße (MiB):",
|
||||
TipSize: "Nur Dateien größer als die Mindestgröße verarbeiten",
|
||||
LblDPI: "Dokument-DPI:",
|
||||
TipDPI: "Auflösung zum Rendern von Dokumenten (PDF, EPUB usw.); Standard verwendet die Originalauflösung",
|
||||
|
||||
LblOutDir: "Ausgabeverzeichnis:",
|
||||
TipOutDir: "Verzeichnis, in das konvertierte Dateien geschrieben werden (erforderlich)",
|
||||
BtnBrowse: "Durchsuchen...",
|
||||
LblSuffix: "Suffix an Ausgabedatei anhängen:",
|
||||
TipSuffix: "Suffix an den Dateinamen anhängen, z. B. dateiname_suffix.cbz",
|
||||
LblArchive: "Archivformat:",
|
||||
TipArchive: "Ausgabecontainer: ZIP (.cbz) oder unkomprimiertes TAR (.cbt)",
|
||||
LblCompression: "Komprimierung:",
|
||||
TipZipLevel: "ZIP-Komprimierung: Speichern deaktiviert sie, 1 ist am schnellsten, 9 am kleinsten",
|
||||
TglCombine: " In einer Datei zusammenfassen",
|
||||
TipCombine: "Alle aufgelisteten Dateien zu einem Archiv zusammenführen",
|
||||
LblOutFile: "Ausgabedatei:",
|
||||
TipOutFile: "Name der zusammengefassten Datei (Standard: erste Eingabe + -combined)",
|
||||
|
||||
LblFormat: "Format:",
|
||||
TipFormat: "Ausgabebildformat für die konvertierten Seiten",
|
||||
LblSize: "Größe:",
|
||||
CueWidth: "Breite",
|
||||
CueHeight: "Höhe",
|
||||
TipWidthHeight: "Wenn Breite oder Höhe nicht gesetzt ist, bleibt das Seitenverhältnis erhalten",
|
||||
TglFit: " Optimale Anpassung",
|
||||
TipFit: "Optimale Anpassung an gewünschte Breite und Höhe",
|
||||
TglNoUpscale: " Nicht vergrößern",
|
||||
TipNoUpscale: "Bilder, die bereits kleiner als die gewünschte Größe sind, nicht vergrößern",
|
||||
LblFilter: "Skalierungsfilter:",
|
||||
LblQuality: "Qualität: ",
|
||||
TipQuality: "Qualität betrifft JPEG, WEBP, AVIF und JXL",
|
||||
LblEffort: "Aufwand:",
|
||||
TipEffort: "Encoder-Geschwindigkeit/-Aufwand (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Verlustfrei",
|
||||
TipLossless: "Verlustfreie Komprimierung (WEBP, AVIF, JXL), ignoriert Qualität",
|
||||
TglGrayscale: " Graustufen",
|
||||
TipGrayscale: "Bilder in Graustufen umwandeln (monochrom)",
|
||||
|
||||
LblBrightness: "Helligkeit: ",
|
||||
TipBrightness: "Die Helligkeit der Bilder anpassen",
|
||||
LblContrast: "Kontrast: ",
|
||||
TipContrast: "Den Kontrast der Bilder anpassen",
|
||||
LblRotate: "Drehen:",
|
||||
TipRotate: "Jede Seite um den angegebenen Winkel im Uhrzeigersinn drehen",
|
||||
|
||||
EffortMethod: "Methode",
|
||||
EffortSpeed: "Geschwindigkeit",
|
||||
EffortEffort: "Aufwand",
|
||||
TipEffortWebp: "WEBP-Methode, höher ist besser/langsamer (0-6, Standard 4)",
|
||||
TipEffortAvif: "AVIF-Geschwindigkeit, höher ist schneller/schlechter (0-10, Standard 10)",
|
||||
TipEffortJxl: "JXL-Aufwand, höher ist besser/langsamer (1-10, Standard 7)",
|
||||
|
||||
BtnAddFiles: "&Dateien hinzufügen...",
|
||||
BtnAddDir: "&Verzeichnis hinzufügen...",
|
||||
BtnRemove: "Entfernen",
|
||||
BtnRemoveAll: "Alle entfernen",
|
||||
BtnThumbnail: "Miniaturbild",
|
||||
BtnCover: "Titelbild",
|
||||
BtnConvert: "&Konvertieren",
|
||||
BtnCancel: "Abbrechen",
|
||||
TipCancel: "Laufenden Vorgang abbrechen (oder Esc drücken)",
|
||||
BtnReset: "Zurücksetzen",
|
||||
TipReset: "Alle Einstellungen auf die Standardwerte zurücksetzen",
|
||||
BtnSave: "Speichern",
|
||||
TipSave: "Aktuelle Einstellungen in einem Profil speichern",
|
||||
BtnCommand: "Befehl",
|
||||
TipCommand: "Die entsprechende Befehlszeile anzeigen",
|
||||
LblProfile: "Profil:",
|
||||
TipProfile: "Ein Einstellungsprofil auswählen",
|
||||
|
||||
TipThumbnail: "Titelbild-Miniaturen extrahieren",
|
||||
TipCover: "Titelbilder extrahieren",
|
||||
TipConvert: "Dateien in das ausgewählte Format konvertieren",
|
||||
|
||||
StatusNeedFilesAndDir: "Dateien hinzufügen und Ausgabeverzeichnis festlegen",
|
||||
StatusNeedFiles: "Dateien hinzufügen",
|
||||
StatusNeedOutDir: "Ausgabeverzeichnis festlegen",
|
||||
StatusFileOf: "Datei %d von %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor ist der schnellste Skalierungsfilter, ohne Antialiasing",
|
||||
FilterBox: "Box-Filter (Mittelung der Pixel)",
|
||||
FilterLinear: "Linear ist der bilineare Filter, glatt und einigermaßen schnell",
|
||||
FilterMitchell: "MitchellNetravali ist ein glatter bikubischer Filter",
|
||||
FilterCatmull: "CatmullRom ist ein scharfer bikubischer Filter",
|
||||
FilterGaussian: "Gaussian ist ein Weichzeichnungsfilter mit Gauß-Funktion, nützlich zur Rauschentfernung",
|
||||
FilterLanczos: "Lanczos ist ein hochwertiger Skalierungsfilter, langsamer als bikubische Filter",
|
||||
|
||||
DlgAddFiles: "Dateien hinzufügen",
|
||||
DlgAddDir: "Verzeichnis hinzufügen",
|
||||
DlgOutputDir: "Ausgabeverzeichnis",
|
||||
DlgOutputFile: "Ausgabedatei",
|
||||
DlgCommandLine: "Befehlszeile",
|
||||
DlgSaveProfile: "Profil speichern",
|
||||
|
||||
ParamName: "Name: %s\n",
|
||||
MsgInvalidNameTitle: "Ungültiger Name",
|
||||
MsgInvalidNameBody: "Der Profilname darf nicht leer sein oder '.' oder ';' enthalten.",
|
||||
|
||||
NoPreview: "Keine Vorschau",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("ENGLISH", map[string]string{
|
||||
ColTitle: "Title",
|
||||
ColType: "Type",
|
||||
ColSize: "Size (MiB)",
|
||||
|
||||
TabInput: "Input",
|
||||
TabOutput: "Output",
|
||||
TabImage: "Image",
|
||||
TabTransform: "Transform",
|
||||
|
||||
LblPage: "Page:",
|
||||
TipPage: "Preview a different page of the selected comic",
|
||||
|
||||
TglRecursive: " Recurse SubDirectories",
|
||||
TipRecursive: "Process subdirectories recursively",
|
||||
TglNoRGB: " Only Grayscale Images",
|
||||
TipNoRGB: "Do not convert images that have RGB colorspace",
|
||||
TglNoCover: " Exclude Cover",
|
||||
TipNoCover: "Do not convert the cover image",
|
||||
TglNoNonImage: " Remove Non-Image Files from the Archive",
|
||||
TipNoNonImage: "Remove .nfo, .xml, .txt files from the archive",
|
||||
TglNoConvert: " Do not Transform or Convert Images",
|
||||
TipNoConvert: "Copy images from archive or directory without modifications",
|
||||
LblMinSize: "Minimum Size (MiB):",
|
||||
TipSize: "Process only files larger than minimum size",
|
||||
LblDPI: "Document DPI:",
|
||||
TipDPI: "Resolution for rendering documents (PDF, EPUB, etc.); Default uses the original resolution",
|
||||
|
||||
LblOutDir: "Output Directory:",
|
||||
TipOutDir: "Directory where converted files are written (required)",
|
||||
BtnBrowse: "Browse...",
|
||||
LblSuffix: "Add Suffix to Output File:",
|
||||
TipSuffix: "Add suffix to filename, i.e. filename_suffix.cbz",
|
||||
LblArchive: "Archive Format:",
|
||||
TipArchive: "Output container: ZIP (.cbz) or uncompressed TAR (.cbt)",
|
||||
LblCompression: "Compression:",
|
||||
TipZipLevel: "ZIP compression: Store disables it, 1 is fastest, 9 is smallest",
|
||||
TglCombine: " Combine into single file",
|
||||
TipCombine: "Merge all listed files into one archive",
|
||||
LblOutFile: "Output File:",
|
||||
TipOutFile: "Combined file name (default: first input + -combined)",
|
||||
|
||||
LblFormat: "Format:",
|
||||
TipFormat: "Output image format for the converted pages",
|
||||
LblSize: "Size:",
|
||||
CueWidth: "width",
|
||||
CueHeight: "height",
|
||||
TipWidthHeight: "If one of, width or height is not set, the image aspect ratio is preserved",
|
||||
TglFit: " Best Fit",
|
||||
TipFit: "Best fit for required width and height",
|
||||
TglNoUpscale: " No Upscale",
|
||||
TipNoUpscale: "Do not enlarge images already smaller than the requested size",
|
||||
LblFilter: "Resize Filter:",
|
||||
LblQuality: "Quality: ",
|
||||
TipQuality: "Quality affects JPEG, WEBP, AVIF and JXL",
|
||||
LblEffort: "Effort:",
|
||||
TipEffort: "Encoder speed/effort (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Lossless",
|
||||
TipLossless: "Lossless compression (WEBP, AVIF, JXL), ignores quality",
|
||||
TglGrayscale: " Grayscale",
|
||||
TipGrayscale: "Convert images to grayscale (monochromatic)",
|
||||
|
||||
LblBrightness: "Brightness: ",
|
||||
TipBrightness: "Adjust the brightness of the images",
|
||||
LblContrast: "Contrast: ",
|
||||
TipContrast: "Adjust the contrast of the images",
|
||||
LblRotate: "Rotate:",
|
||||
TipRotate: "Rotate every page clockwise by the given angle in degrees",
|
||||
|
||||
EffortMethod: "Method",
|
||||
EffortSpeed: "Speed",
|
||||
EffortEffort: "Effort",
|
||||
TipEffortWebp: "WEBP method, higher is better/slower (0-6, default 4)",
|
||||
TipEffortAvif: "AVIF speed, higher is faster/worse (0-10, default 10)",
|
||||
TipEffortJxl: "JXL effort, higher is better/slower (1-10, default 7)",
|
||||
|
||||
BtnAddFiles: "Add &Files...",
|
||||
BtnAddDir: "Add &Dir...",
|
||||
BtnRemove: "Remove",
|
||||
BtnRemoveAll: "Remove All",
|
||||
BtnThumbnail: "Thumbnail",
|
||||
BtnCover: "Cover",
|
||||
BtnConvert: "&Convert",
|
||||
BtnCancel: "Cancel",
|
||||
TipCancel: "Cancel the running operation (or press Esc)",
|
||||
BtnReset: "Reset",
|
||||
TipReset: "Restore all settings to their defaults",
|
||||
BtnSave: "Save",
|
||||
TipSave: "Save current settings to a profile",
|
||||
BtnCommand: "Command",
|
||||
TipCommand: "Show the equivalent command line",
|
||||
LblProfile: "Profile:",
|
||||
TipProfile: "Select a settings profile",
|
||||
|
||||
TipThumbnail: "Extract cover thumbnails",
|
||||
TipCover: "Extract covers",
|
||||
TipConvert: "Convert files to the selected format",
|
||||
|
||||
StatusNeedFilesAndDir: "Add files and set output directory",
|
||||
StatusNeedFiles: "Add files",
|
||||
StatusNeedOutDir: "Set output directory",
|
||||
StatusFileOf: "File %d of %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor is the fastest resampling filter, no antialiasing",
|
||||
FilterBox: "Box filter (averaging pixels)",
|
||||
FilterLinear: "Linear is the bilinear filter, smooth and reasonably fast",
|
||||
FilterMitchell: "MitchellNetravali is a smooth bicubic filter",
|
||||
FilterCatmull: "CatmullRom is a sharp bicubic filter",
|
||||
FilterGaussian: "Gaussian is a blurring filter that uses gaussian function, useful for noise removal",
|
||||
FilterLanczos: "Lanczos is a high-quality resampling filter, it's slower than cubic filters",
|
||||
|
||||
DlgAddFiles: "Add Files",
|
||||
DlgAddDir: "Add Directory",
|
||||
DlgOutputDir: "Output Directory",
|
||||
DlgOutputFile: "Output File",
|
||||
DlgCommandLine: "Command Line",
|
||||
DlgSaveProfile: "Save Profile",
|
||||
|
||||
ParamName: "Name: %s\n",
|
||||
MsgInvalidNameTitle: "Invalid Name",
|
||||
MsgInvalidNameBody: "Profile name must not be empty or contain '.' or ';'.",
|
||||
|
||||
NoPreview: "No preview",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("SPANISH", map[string]string{
|
||||
ColTitle: "Título",
|
||||
ColType: "Tipo",
|
||||
ColSize: "Tamaño (MiB)",
|
||||
|
||||
TabInput: "Entrada",
|
||||
TabOutput: "Salida",
|
||||
TabImage: "Imagen",
|
||||
TabTransform: "Transformar",
|
||||
|
||||
LblPage: "Página:",
|
||||
TipPage: "Previsualizar otra página del cómic seleccionado",
|
||||
|
||||
TglRecursive: " Recorrer subdirectorios",
|
||||
TipRecursive: "Procesar subdirectorios de forma recursiva",
|
||||
TglNoRGB: " Solo imágenes en escala de grises",
|
||||
TipNoRGB: "No convertir imágenes que tengan espacio de color RGB",
|
||||
TglNoCover: " Excluir la portada",
|
||||
TipNoCover: "No convertir la imagen de portada",
|
||||
TglNoNonImage: " Eliminar archivos que no sean imágenes del archivo",
|
||||
TipNoNonImage: "Eliminar archivos .nfo, .xml, .txt del archivo",
|
||||
TglNoConvert: " No transformar ni convertir imágenes",
|
||||
TipNoConvert: "Copiar imágenes del archivo o directorio sin modificaciones",
|
||||
LblMinSize: "Tamaño mínimo (MiB):",
|
||||
TipSize: "Procesar solo archivos mayores que el tamaño mínimo",
|
||||
LblDPI: "DPI del documento:",
|
||||
TipDPI: "Resolución para renderizar documentos (PDF, EPUB, etc.); el valor predeterminado usa la resolución original",
|
||||
|
||||
LblOutDir: "Directorio de salida:",
|
||||
TipOutDir: "Directorio donde se escriben los archivos convertidos (obligatorio)",
|
||||
BtnBrowse: "Examinar...",
|
||||
LblSuffix: "Añadir sufijo al archivo de salida:",
|
||||
TipSuffix: "Añadir sufijo al nombre de archivo, p. ej. nombrearchivo_sufijo.cbz",
|
||||
LblArchive: "Formato de archivo:",
|
||||
TipArchive: "Contenedor de salida: ZIP (.cbz) o TAR sin comprimir (.cbt)",
|
||||
LblCompression: "Compresión:",
|
||||
TipZipLevel: "Compresión ZIP: Almacenar la desactiva, 1 es la más rápida, 9 la más pequeña",
|
||||
TglCombine: " Combinar en un solo archivo",
|
||||
TipCombine: "Fusionar todos los archivos listados en un solo archivo",
|
||||
LblOutFile: "Archivo de salida:",
|
||||
TipOutFile: "Nombre del archivo combinado (predeterminado: primera entrada + -combined)",
|
||||
|
||||
LblFormat: "Formato:",
|
||||
TipFormat: "Formato de imagen de salida para las páginas convertidas",
|
||||
LblSize: "Tamaño:",
|
||||
CueWidth: "ancho",
|
||||
CueHeight: "alto",
|
||||
TipWidthHeight: "Si no se define el ancho o el alto, se conserva la relación de aspecto de la imagen",
|
||||
TglFit: " Ajuste óptimo",
|
||||
TipFit: "Ajuste óptimo al ancho y alto requeridos",
|
||||
TglNoUpscale: " No ampliar",
|
||||
TipNoUpscale: "No ampliar imágenes que ya son más pequeñas que el tamaño solicitado",
|
||||
LblFilter: "Filtro de redimensionado:",
|
||||
LblQuality: "Calidad: ",
|
||||
TipQuality: "La calidad afecta a JPEG, WEBP, AVIF y JXL",
|
||||
LblEffort: "Esfuerzo:",
|
||||
TipEffort: "Velocidad/esfuerzo del codificador (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Sin pérdidas",
|
||||
TipLossless: "Compresión sin pérdidas (WEBP, AVIF, JXL), ignora la calidad",
|
||||
TglGrayscale: " Escala de grises",
|
||||
TipGrayscale: "Convertir imágenes a escala de grises (monocromático)",
|
||||
|
||||
LblBrightness: "Brillo: ",
|
||||
TipBrightness: "Ajustar el brillo de las imágenes",
|
||||
LblContrast: "Contraste: ",
|
||||
TipContrast: "Ajustar el contraste de las imágenes",
|
||||
LblRotate: "Rotar:",
|
||||
TipRotate: "Rotar cada página en sentido horario el ángulo dado en grados",
|
||||
|
||||
EffortMethod: "Método",
|
||||
EffortSpeed: "Velocidad",
|
||||
EffortEffort: "Esfuerzo",
|
||||
TipEffortWebp: "Método WEBP, más alto es mejor/más lento (0-6, predeterminado 4)",
|
||||
TipEffortAvif: "Velocidad AVIF, más alto es más rápido/peor (0-10, predeterminado 10)",
|
||||
TipEffortJxl: "Esfuerzo JXL, más alto es mejor/más lento (1-10, predeterminado 7)",
|
||||
|
||||
BtnAddFiles: "Añadir &archivos...",
|
||||
BtnAddDir: "Añadir &directorio...",
|
||||
BtnRemove: "Eliminar",
|
||||
BtnRemoveAll: "Eliminar todo",
|
||||
BtnThumbnail: "Miniatura",
|
||||
BtnCover: "Portada",
|
||||
BtnConvert: "&Convertir",
|
||||
BtnCancel: "Cancelar",
|
||||
TipCancel: "Cancelar la operación en curso (o pulsar Esc)",
|
||||
BtnReset: "Restablecer",
|
||||
TipReset: "Restaurar todos los ajustes a sus valores predeterminados",
|
||||
BtnSave: "Guardar",
|
||||
TipSave: "Guardar los ajustes actuales en un perfil",
|
||||
BtnCommand: "Comando",
|
||||
TipCommand: "Mostrar la línea de comandos equivalente",
|
||||
LblProfile: "Perfil:",
|
||||
TipProfile: "Seleccionar un perfil de ajustes",
|
||||
|
||||
TipThumbnail: "Extraer miniaturas de portada",
|
||||
TipCover: "Extraer portadas",
|
||||
TipConvert: "Convertir archivos al formato seleccionado",
|
||||
|
||||
StatusNeedFilesAndDir: "Añadir archivos y establecer el directorio de salida",
|
||||
StatusNeedFiles: "Añadir archivos",
|
||||
StatusNeedOutDir: "Establecer el directorio de salida",
|
||||
StatusFileOf: "Archivo %d de %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor es el filtro de remuestreo más rápido, sin antialiasing",
|
||||
FilterBox: "Filtro Box (promedio de píxeles)",
|
||||
FilterLinear: "Linear es el filtro bilineal, suave y razonablemente rápido",
|
||||
FilterMitchell: "MitchellNetravali es un filtro bicúbico suave",
|
||||
FilterCatmull: "CatmullRom es un filtro bicúbico nítido",
|
||||
FilterGaussian: "Gaussian es un filtro de desenfoque que usa la función gaussiana, útil para eliminar ruido",
|
||||
FilterLanczos: "Lanczos es un filtro de remuestreo de alta calidad, más lento que los filtros cúbicos",
|
||||
|
||||
DlgAddFiles: "Añadir archivos",
|
||||
DlgAddDir: "Añadir directorio",
|
||||
DlgOutputDir: "Directorio de salida",
|
||||
DlgOutputFile: "Archivo de salida",
|
||||
DlgCommandLine: "Línea de comandos",
|
||||
DlgSaveProfile: "Guardar perfil",
|
||||
|
||||
ParamName: "Nombre: %s\n",
|
||||
MsgInvalidNameTitle: "Nombre no válido",
|
||||
MsgInvalidNameBody: "El nombre del perfil no debe estar vacío ni contener '.' o ';'.",
|
||||
|
||||
NoPreview: "Sin vista previa",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("FRENCH", map[string]string{
|
||||
ColTitle: "Titre",
|
||||
ColType: "Type",
|
||||
ColSize: "Taille (Mio)",
|
||||
|
||||
TabInput: "Entrée",
|
||||
TabOutput: "Sortie",
|
||||
TabImage: "Image",
|
||||
TabTransform: "Transformation",
|
||||
|
||||
LblPage: "Page :",
|
||||
TipPage: "Prévisualiser une autre page de la bande dessinée sélectionnée",
|
||||
|
||||
TglRecursive: " Parcourir les sous-dossiers",
|
||||
TipRecursive: "Traiter les sous-dossiers de manière récursive",
|
||||
TglNoRGB: " Images en niveaux de gris uniquement",
|
||||
TipNoRGB: "Ne pas convertir les images ayant un espace colorimétrique RVB",
|
||||
TglNoCover: " Exclure la couverture",
|
||||
TipNoCover: "Ne pas convertir l'image de couverture",
|
||||
TglNoNonImage: " Supprimer les fichiers non-image de l'archive",
|
||||
TipNoNonImage: "Supprimer les fichiers .nfo, .xml, .txt de l'archive",
|
||||
TglNoConvert: " Ne pas transformer ni convertir les images",
|
||||
TipNoConvert: "Copier les images de l'archive ou du dossier sans modifications",
|
||||
LblMinSize: "Taille minimale (Mio) :",
|
||||
TipSize: "Ne traiter que les fichiers plus grands que la taille minimale",
|
||||
LblDPI: "DPI du document :",
|
||||
TipDPI: "Résolution pour le rendu des documents (PDF, EPUB, etc.) ; la valeur par défaut utilise la résolution d'origine",
|
||||
|
||||
LblOutDir: "Dossier de sortie :",
|
||||
TipOutDir: "Dossier où les fichiers convertis sont écrits (obligatoire)",
|
||||
BtnBrowse: "Parcourir...",
|
||||
LblSuffix: "Ajouter un suffixe au fichier de sortie :",
|
||||
TipSuffix: "Ajouter un suffixe au nom de fichier, par ex. nomfichier_suffixe.cbz",
|
||||
LblArchive: "Format d'archive :",
|
||||
TipArchive: "Conteneur de sortie : ZIP (.cbz) ou TAR non compressé (.cbt)",
|
||||
LblCompression: "Compression :",
|
||||
TipZipLevel: "Compression ZIP : Stocker la désactive, 1 est le plus rapide, 9 le plus petit",
|
||||
TglCombine: " Combiner en un seul fichier",
|
||||
TipCombine: "Fusionner tous les fichiers listés en une seule archive",
|
||||
LblOutFile: "Fichier de sortie :",
|
||||
TipOutFile: "Nom du fichier combiné (par défaut : première entrée + -combined)",
|
||||
|
||||
LblFormat: "Format :",
|
||||
TipFormat: "Format d'image de sortie pour les pages converties",
|
||||
LblSize: "Taille :",
|
||||
CueWidth: "largeur",
|
||||
CueHeight: "hauteur",
|
||||
TipWidthHeight: "Si la largeur ou la hauteur n'est pas définie, le rapport d'aspect de l'image est conservé",
|
||||
TglFit: " Ajustement optimal",
|
||||
TipFit: "Ajustement optimal à la largeur et la hauteur requises",
|
||||
TglNoUpscale: " Ne pas agrandir",
|
||||
TipNoUpscale: "Ne pas agrandir les images déjà plus petites que la taille demandée",
|
||||
LblFilter: "Filtre de redimensionnement :",
|
||||
LblQuality: "Qualité : ",
|
||||
TipQuality: "La qualité concerne JPEG, WEBP, AVIF et JXL",
|
||||
LblEffort: "Effort :",
|
||||
TipEffort: "Vitesse/effort de l'encodeur (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Sans perte",
|
||||
TipLossless: "Compression sans perte (WEBP, AVIF, JXL), ignore la qualité",
|
||||
TglGrayscale: " Niveaux de gris",
|
||||
TipGrayscale: "Convertir les images en niveaux de gris (monochrome)",
|
||||
|
||||
LblBrightness: "Luminosité : ",
|
||||
TipBrightness: "Ajuster la luminosité des images",
|
||||
LblContrast: "Contraste : ",
|
||||
TipContrast: "Ajuster le contraste des images",
|
||||
LblRotate: "Rotation :",
|
||||
TipRotate: "Faire pivoter chaque page dans le sens horaire de l'angle donné en degrés",
|
||||
|
||||
EffortMethod: "Méthode",
|
||||
EffortSpeed: "Vitesse",
|
||||
EffortEffort: "Effort",
|
||||
TipEffortWebp: "Méthode WEBP, plus élevé est meilleur/plus lent (0-6, défaut 4)",
|
||||
TipEffortAvif: "Vitesse AVIF, plus élevé est plus rapide/moins bon (0-10, défaut 10)",
|
||||
TipEffortJxl: "Effort JXL, plus élevé est meilleur/plus lent (1-10, défaut 7)",
|
||||
|
||||
BtnAddFiles: "Ajouter des &fichiers...",
|
||||
BtnAddDir: "Ajouter un &dossier...",
|
||||
BtnRemove: "Supprimer",
|
||||
BtnRemoveAll: "Tout supprimer",
|
||||
BtnThumbnail: "Miniature",
|
||||
BtnCover: "Couverture",
|
||||
BtnConvert: "&Convertir",
|
||||
BtnCancel: "Annuler",
|
||||
TipCancel: "Annuler l'opération en cours (ou appuyer sur Échap)",
|
||||
BtnReset: "Réinitialiser",
|
||||
TipReset: "Restaurer tous les paramètres à leurs valeurs par défaut",
|
||||
BtnSave: "Enregistrer",
|
||||
TipSave: "Enregistrer les paramètres actuels dans un profil",
|
||||
BtnCommand: "Commande",
|
||||
TipCommand: "Afficher la ligne de commande équivalente",
|
||||
LblProfile: "Profil :",
|
||||
TipProfile: "Sélectionner un profil de paramètres",
|
||||
|
||||
TipThumbnail: "Extraire les miniatures de couverture",
|
||||
TipCover: "Extraire les couvertures",
|
||||
TipConvert: "Convertir les fichiers vers le format sélectionné",
|
||||
|
||||
StatusNeedFilesAndDir: "Ajouter des fichiers et définir le dossier de sortie",
|
||||
StatusNeedFiles: "Ajouter des fichiers",
|
||||
StatusNeedOutDir: "Définir le dossier de sortie",
|
||||
StatusFileOf: "Fichier %d sur %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor est le filtre de rééchantillonnage le plus rapide, sans anticrénelage",
|
||||
FilterBox: "Filtre Box (moyenne des pixels)",
|
||||
FilterLinear: "Linear est le filtre bilinéaire, lisse et raisonnablement rapide",
|
||||
FilterMitchell: "MitchellNetravali est un filtre bicubique lisse",
|
||||
FilterCatmull: "CatmullRom est un filtre bicubique net",
|
||||
FilterGaussian: "Gaussian est un filtre de flou utilisant la fonction gaussienne, utile pour la réduction du bruit",
|
||||
FilterLanczos: "Lanczos est un filtre de rééchantillonnage de haute qualité, plus lent que les filtres cubiques",
|
||||
|
||||
DlgAddFiles: "Ajouter des fichiers",
|
||||
DlgAddDir: "Ajouter un dossier",
|
||||
DlgOutputDir: "Dossier de sortie",
|
||||
DlgOutputFile: "Fichier de sortie",
|
||||
DlgCommandLine: "Ligne de commande",
|
||||
DlgSaveProfile: "Enregistrer le profil",
|
||||
|
||||
ParamName: "Nom : %s\n",
|
||||
MsgInvalidNameTitle: "Nom invalide",
|
||||
MsgInvalidNameBody: "Le nom du profil ne doit pas être vide ni contenir '.' ou ';'.",
|
||||
|
||||
NoPreview: "Aucun aperçu",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("ITALIAN", map[string]string{
|
||||
ColTitle: "Titolo",
|
||||
ColType: "Tipo",
|
||||
ColSize: "Dimensione (MiB)",
|
||||
|
||||
TabInput: "Ingresso",
|
||||
TabOutput: "Uscita",
|
||||
TabImage: "Immagine",
|
||||
TabTransform: "Trasforma",
|
||||
|
||||
LblPage: "Pagina:",
|
||||
TipPage: "Anteprima di un'altra pagina del fumetto selezionato",
|
||||
|
||||
TglRecursive: " Esplora sottocartelle",
|
||||
TipRecursive: "Elabora le sottocartelle in modo ricorsivo",
|
||||
TglNoRGB: " Solo immagini in scala di grigi",
|
||||
TipNoRGB: "Non convertire le immagini con spazio colore RGB",
|
||||
TglNoCover: " Escludi la copertina",
|
||||
TipNoCover: "Non convertire l'immagine di copertina",
|
||||
TglNoNonImage: " Rimuovi i file non immagine dall'archivio",
|
||||
TipNoNonImage: "Rimuovi i file .nfo, .xml, .txt dall'archivio",
|
||||
TglNoConvert: " Non trasformare né convertire le immagini",
|
||||
TipNoConvert: "Copia le immagini dall'archivio o dalla cartella senza modifiche",
|
||||
LblMinSize: "Dimensione minima (MiB):",
|
||||
TipSize: "Elabora solo i file più grandi della dimensione minima",
|
||||
LblDPI: "DPI del documento:",
|
||||
TipDPI: "Risoluzione per il rendering dei documenti (PDF, EPUB, ecc.); il valore predefinito usa la risoluzione originale",
|
||||
|
||||
LblOutDir: "Cartella di uscita:",
|
||||
TipOutDir: "Cartella in cui vengono scritti i file convertiti (obbligatoria)",
|
||||
BtnBrowse: "Sfoglia...",
|
||||
LblSuffix: "Aggiungi suffisso al file di uscita:",
|
||||
TipSuffix: "Aggiungi un suffisso al nome del file, ad es. nomefile_suffisso.cbz",
|
||||
LblArchive: "Formato archivio:",
|
||||
TipArchive: "Contenitore di uscita: ZIP (.cbz) o TAR non compresso (.cbt)",
|
||||
LblCompression: "Compressione:",
|
||||
TipZipLevel: "Compressione ZIP: Memorizza la disattiva, 1 è la più veloce, 9 la più piccola",
|
||||
TglCombine: " Combina in un unico file",
|
||||
TipCombine: "Unisci tutti i file elencati in un unico archivio",
|
||||
LblOutFile: "File di uscita:",
|
||||
TipOutFile: "Nome del file combinato (predefinito: primo ingresso + -combined)",
|
||||
|
||||
LblFormat: "Formato:",
|
||||
TipFormat: "Formato immagine di uscita per le pagine convertite",
|
||||
LblSize: "Dimensione:",
|
||||
CueWidth: "larghezza",
|
||||
CueHeight: "altezza",
|
||||
TipWidthHeight: "Se la larghezza o l'altezza non è impostata, viene mantenuto il rapporto d'aspetto dell'immagine",
|
||||
TglFit: " Adattamento ottimale",
|
||||
TipFit: "Adattamento ottimale alla larghezza e all'altezza richieste",
|
||||
TglNoUpscale: " Non ingrandire",
|
||||
TipNoUpscale: "Non ingrandire le immagini già più piccole della dimensione richiesta",
|
||||
LblFilter: "Filtro di ridimensionamento:",
|
||||
LblQuality: "Qualità: ",
|
||||
TipQuality: "La qualità riguarda JPEG, WEBP, AVIF e JXL",
|
||||
LblEffort: "Sforzo:",
|
||||
TipEffort: "Velocità/sforzo del codificatore (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Senza perdita",
|
||||
TipLossless: "Compressione senza perdita (WEBP, AVIF, JXL), ignora la qualità",
|
||||
TglGrayscale: " Scala di grigi",
|
||||
TipGrayscale: "Converti le immagini in scala di grigi (monocromatico)",
|
||||
|
||||
LblBrightness: "Luminosità: ",
|
||||
TipBrightness: "Regola la luminosità delle immagini",
|
||||
LblContrast: "Contrasto: ",
|
||||
TipContrast: "Regola il contrasto delle immagini",
|
||||
LblRotate: "Ruota:",
|
||||
TipRotate: "Ruota ogni pagina in senso orario dell'angolo indicato in gradi",
|
||||
|
||||
EffortMethod: "Metodo",
|
||||
EffortSpeed: "Velocità",
|
||||
EffortEffort: "Sforzo",
|
||||
TipEffortWebp: "Metodo WEBP, più alto è migliore/più lento (0-6, predefinito 4)",
|
||||
TipEffortAvif: "Velocità AVIF, più alto è più veloce/peggiore (0-10, predefinito 10)",
|
||||
TipEffortJxl: "Sforzo JXL, più alto è migliore/più lento (1-10, predefinito 7)",
|
||||
|
||||
BtnAddFiles: "Aggiungi &file...",
|
||||
BtnAddDir: "Aggiungi &cartella...",
|
||||
BtnRemove: "Rimuovi",
|
||||
BtnRemoveAll: "Rimuovi tutto",
|
||||
BtnThumbnail: "Miniatura",
|
||||
BtnCover: "Copertina",
|
||||
BtnConvert: "&Converti",
|
||||
BtnCancel: "Annulla",
|
||||
TipCancel: "Annulla l'operazione in corso (o premi Esc)",
|
||||
BtnReset: "Ripristina",
|
||||
TipReset: "Ripristina tutte le impostazioni ai valori predefiniti",
|
||||
BtnSave: "Salva",
|
||||
TipSave: "Salva le impostazioni correnti in un profilo",
|
||||
BtnCommand: "Comando",
|
||||
TipCommand: "Mostra la riga di comando equivalente",
|
||||
LblProfile: "Profilo:",
|
||||
TipProfile: "Seleziona un profilo di impostazioni",
|
||||
|
||||
TipThumbnail: "Estrai le miniature delle copertine",
|
||||
TipCover: "Estrai le copertine",
|
||||
TipConvert: "Converti i file nel formato selezionato",
|
||||
|
||||
StatusNeedFilesAndDir: "Aggiungi file e imposta la cartella di uscita",
|
||||
StatusNeedFiles: "Aggiungi file",
|
||||
StatusNeedOutDir: "Imposta la cartella di uscita",
|
||||
StatusFileOf: "File %d di %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor è il filtro di ricampionamento più veloce, senza antialiasing",
|
||||
FilterBox: "Filtro Box (media dei pixel)",
|
||||
FilterLinear: "Linear è il filtro bilineare, morbido e ragionevolmente veloce",
|
||||
FilterMitchell: "MitchellNetravali è un filtro bicubico morbido",
|
||||
FilterCatmull: "CatmullRom è un filtro bicubico nitido",
|
||||
FilterGaussian: "Gaussian è un filtro di sfocatura che usa la funzione gaussiana, utile per la rimozione del rumore",
|
||||
FilterLanczos: "Lanczos è un filtro di ricampionamento di alta qualità, più lento dei filtri cubici",
|
||||
|
||||
DlgAddFiles: "Aggiungi file",
|
||||
DlgAddDir: "Aggiungi cartella",
|
||||
DlgOutputDir: "Cartella di uscita",
|
||||
DlgOutputFile: "File di uscita",
|
||||
DlgCommandLine: "Riga di comando",
|
||||
DlgSaveProfile: "Salva profilo",
|
||||
|
||||
ParamName: "Nome: %s\n",
|
||||
MsgInvalidNameTitle: "Nome non valido",
|
||||
MsgInvalidNameBody: "Il nome del profilo non deve essere vuoto né contenere '.' o ';'.",
|
||||
|
||||
NoPreview: "Nessuna anteprima",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("JAPANESE", map[string]string{
|
||||
ColTitle: "タイトル",
|
||||
ColType: "種類",
|
||||
ColSize: "サイズ (MiB)",
|
||||
|
||||
TabInput: "入力",
|
||||
TabOutput: "出力",
|
||||
TabImage: "画像",
|
||||
TabTransform: "変換",
|
||||
|
||||
LblPage: "ページ:",
|
||||
TipPage: "選択したコミックの別のページをプレビュー",
|
||||
|
||||
TglRecursive: " サブディレクトリを再帰的に処理",
|
||||
TipRecursive: "サブディレクトリを再帰的に処理する",
|
||||
TglNoRGB: " グレースケール画像のみ",
|
||||
TipNoRGB: "RGB 色空間の画像を変換しない",
|
||||
TglNoCover: " 表紙を除外",
|
||||
TipNoCover: "表紙画像を変換しない",
|
||||
TglNoNonImage: " アーカイブから画像以外のファイルを削除",
|
||||
TipNoNonImage: "アーカイブから .nfo、.xml、.txt ファイルを削除する",
|
||||
TglNoConvert: " 画像を変換・変形しない",
|
||||
TipNoConvert: "アーカイブまたはディレクトリから画像を変更せずにコピーする",
|
||||
LblMinSize: "最小サイズ (MiB):",
|
||||
TipSize: "最小サイズより大きいファイルのみ処理する",
|
||||
LblDPI: "ドキュメント DPI:",
|
||||
TipDPI: "ドキュメント(PDF、EPUB など)をレンダリングする解像度。既定値は元の解像度を使用します",
|
||||
|
||||
LblOutDir: "出力ディレクトリ:",
|
||||
TipOutDir: "変換されたファイルが書き込まれるディレクトリ(必須)",
|
||||
BtnBrowse: "参照...",
|
||||
LblSuffix: "出力ファイルにサフィックスを追加:",
|
||||
TipSuffix: "ファイル名にサフィックスを追加する。例: filename_suffix.cbz",
|
||||
LblArchive: "アーカイブ形式:",
|
||||
TipArchive: "出力コンテナ: ZIP (.cbz) または非圧縮の TAR (.cbt)",
|
||||
LblCompression: "圧縮:",
|
||||
TipZipLevel: "ZIP 圧縮: 「保存」で無効化、1 が最速、9 が最小",
|
||||
TglCombine: " 単一ファイルに結合",
|
||||
TipCombine: "リストされたすべてのファイルを 1 つのアーカイブに結合する",
|
||||
LblOutFile: "出力ファイル:",
|
||||
TipOutFile: "結合ファイル名(既定: 最初の入力 + -combined)",
|
||||
|
||||
LblFormat: "形式:",
|
||||
TipFormat: "変換されるページの出力画像形式",
|
||||
LblSize: "サイズ:",
|
||||
CueWidth: "幅",
|
||||
CueHeight: "高さ",
|
||||
TipWidthHeight: "幅または高さが設定されていない場合、画像の縦横比が保持されます",
|
||||
TglFit: " 最適化",
|
||||
TipFit: "必要な幅と高さに最適化",
|
||||
TglNoUpscale: " 拡大しない",
|
||||
TipNoUpscale: "要求されたサイズより既に小さい画像を拡大しない",
|
||||
LblFilter: "リサイズフィルター:",
|
||||
LblQuality: "品質:",
|
||||
TipQuality: "品質は JPEG、WEBP、AVIF、JXL に影響します",
|
||||
LblEffort: "労力:",
|
||||
TipEffort: "エンコーダーの速度/労力(WEBP、AVIF、JXL)",
|
||||
TglLossless: " ロスレス",
|
||||
TipLossless: "ロスレス圧縮(WEBP、AVIF、JXL)、品質を無視します",
|
||||
TglGrayscale: " グレースケール",
|
||||
TipGrayscale: "画像をグレースケール(モノクロ)に変換する",
|
||||
|
||||
LblBrightness: "明るさ:",
|
||||
TipBrightness: "画像の明るさを調整する",
|
||||
LblContrast: "コントラスト:",
|
||||
TipContrast: "画像のコントラストを調整する",
|
||||
LblRotate: "回転:",
|
||||
TipRotate: "各ページを指定した角度(度)だけ時計回りに回転する",
|
||||
|
||||
EffortMethod: "方式",
|
||||
EffortSpeed: "速度",
|
||||
EffortEffort: "労力",
|
||||
TipEffortWebp: "WEBP 方式、高いほど高品質/低速(0-6、既定 4)",
|
||||
TipEffortAvif: "AVIF 速度、高いほど高速/低品質(0-10、既定 10)",
|
||||
TipEffortJxl: "JXL 労力、高いほど高品質/低速(1-10、既定 7)",
|
||||
|
||||
BtnAddFiles: "ファイルを追加(&F)...",
|
||||
BtnAddDir: "ディレクトリを追加(&D)...",
|
||||
BtnRemove: "削除",
|
||||
BtnRemoveAll: "すべて削除",
|
||||
BtnThumbnail: "サムネイル",
|
||||
BtnCover: "表紙",
|
||||
BtnConvert: "変換(&C)",
|
||||
BtnCancel: "キャンセル",
|
||||
TipCancel: "実行中の操作をキャンセル(または Esc キーを押す)",
|
||||
BtnReset: "リセット",
|
||||
TipReset: "すべての設定を既定値に戻す",
|
||||
BtnSave: "保存",
|
||||
TipSave: "現在の設定をプロファイルに保存する",
|
||||
BtnCommand: "コマンド",
|
||||
TipCommand: "同等のコマンドラインを表示する",
|
||||
LblProfile: "プロファイル:",
|
||||
TipProfile: "設定プロファイルを選択する",
|
||||
|
||||
TipThumbnail: "表紙のサムネイルを抽出する",
|
||||
TipCover: "表紙を抽出する",
|
||||
TipConvert: "ファイルを選択した形式に変換する",
|
||||
|
||||
StatusNeedFilesAndDir: "ファイルを追加し、出力ディレクトリを設定してください",
|
||||
StatusNeedFiles: "ファイルを追加してください",
|
||||
StatusNeedOutDir: "出力ディレクトリを設定してください",
|
||||
StatusFileOf: "ファイル %d / %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor は最速のリサンプリングフィルターで、アンチエイリアスなし",
|
||||
FilterBox: "Box フィルター(ピクセルの平均化)",
|
||||
FilterLinear: "Linear はバイリニアフィルターで、滑らかでそれなりに高速",
|
||||
FilterMitchell: "MitchellNetravali は滑らかなバイキュービックフィルター",
|
||||
FilterCatmull: "CatmullRom はシャープなバイキュービックフィルター",
|
||||
FilterGaussian: "Gaussian はガウス関数を使用するぼかしフィルターで、ノイズ除去に便利",
|
||||
FilterLanczos: "Lanczos は高品質なリサンプリングフィルターで、キュービックフィルターより低速",
|
||||
|
||||
DlgAddFiles: "ファイルを追加",
|
||||
DlgAddDir: "ディレクトリを追加",
|
||||
DlgOutputDir: "出力ディレクトリ",
|
||||
DlgOutputFile: "出力ファイル",
|
||||
DlgCommandLine: "コマンドライン",
|
||||
DlgSaveProfile: "プロファイルを保存",
|
||||
|
||||
ParamName: "名前:%s\n",
|
||||
MsgInvalidNameTitle: "無効な名前",
|
||||
MsgInvalidNameBody: "プロファイル名は空にすることはできず、'.' または ';' を含めることはできません。",
|
||||
|
||||
NoPreview: "プレビューなし",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("PORTUGUESE", map[string]string{
|
||||
ColTitle: "Título",
|
||||
ColType: "Tipo",
|
||||
ColSize: "Tamanho (MiB)",
|
||||
|
||||
TabInput: "Entrada",
|
||||
TabOutput: "Saída",
|
||||
TabImage: "Imagem",
|
||||
TabTransform: "Transformar",
|
||||
|
||||
LblPage: "Página:",
|
||||
TipPage: "Pré-visualizar outra página da revista em quadrinhos selecionada",
|
||||
|
||||
TglRecursive: " Percorrer subdiretórios",
|
||||
TipRecursive: "Processar subdiretórios recursivamente",
|
||||
TglNoRGB: " Apenas imagens em escala de cinza",
|
||||
TipNoRGB: "Não converter imagens que tenham espaço de cor RGB",
|
||||
TglNoCover: " Excluir a capa",
|
||||
TipNoCover: "Não converter a imagem de capa",
|
||||
TglNoNonImage: " Remover arquivos não-imagem do arquivo",
|
||||
TipNoNonImage: "Remover arquivos .nfo, .xml, .txt do arquivo",
|
||||
TglNoConvert: " Não transformar nem converter imagens",
|
||||
TipNoConvert: "Copiar imagens do arquivo ou diretório sem modificações",
|
||||
LblMinSize: "Tamanho mínimo (MiB):",
|
||||
TipSize: "Processar apenas arquivos maiores que o tamanho mínimo",
|
||||
LblDPI: "DPI do documento:",
|
||||
TipDPI: "Resolução para renderizar documentos (PDF, EPUB, etc.); o padrão usa a resolução original",
|
||||
|
||||
LblOutDir: "Diretório de saída:",
|
||||
TipOutDir: "Diretório onde os arquivos convertidos são gravados (obrigatório)",
|
||||
BtnBrowse: "Procurar...",
|
||||
LblSuffix: "Adicionar sufixo ao arquivo de saída:",
|
||||
TipSuffix: "Adicionar sufixo ao nome do arquivo, p. ex. nomearquivo_sufixo.cbz",
|
||||
LblArchive: "Formato de arquivo:",
|
||||
TipArchive: "Contêiner de saída: ZIP (.cbz) ou TAR não comprimido (.cbt)",
|
||||
LblCompression: "Compressão:",
|
||||
TipZipLevel: "Compressão ZIP: Armazenar a desativa, 1 é a mais rápida, 9 a menor",
|
||||
TglCombine: " Combinar em um único arquivo",
|
||||
TipCombine: "Mesclar todos os arquivos listados em um único arquivo",
|
||||
LblOutFile: "Arquivo de saída:",
|
||||
TipOutFile: "Nome do arquivo combinado (padrão: primeira entrada + -combined)",
|
||||
|
||||
LblFormat: "Formato:",
|
||||
TipFormat: "Formato de imagem de saída para as páginas convertidas",
|
||||
LblSize: "Tamanho:",
|
||||
CueWidth: "largura",
|
||||
CueHeight: "altura",
|
||||
TipWidthHeight: "Se a largura ou a altura não for definida, a proporção da imagem é preservada",
|
||||
TglFit: " Melhor ajuste",
|
||||
TipFit: "Melhor ajuste à largura e altura desejadas",
|
||||
TglNoUpscale: " Não ampliar",
|
||||
TipNoUpscale: "Não ampliar imagens que já são menores que o tamanho solicitado",
|
||||
LblFilter: "Filtro de redimensionamento:",
|
||||
LblQuality: "Qualidade: ",
|
||||
TipQuality: "A qualidade afeta JPEG, WEBP, AVIF e JXL",
|
||||
LblEffort: "Esforço:",
|
||||
TipEffort: "Velocidade/esforço do codificador (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Sem perdas",
|
||||
TipLossless: "Compressão sem perdas (WEBP, AVIF, JXL), ignora a qualidade",
|
||||
TglGrayscale: " Escala de cinza",
|
||||
TipGrayscale: "Converter imagens para escala de cinza (monocromático)",
|
||||
|
||||
LblBrightness: "Brilho: ",
|
||||
TipBrightness: "Ajustar o brilho das imagens",
|
||||
LblContrast: "Contraste: ",
|
||||
TipContrast: "Ajustar o contraste das imagens",
|
||||
LblRotate: "Girar:",
|
||||
TipRotate: "Girar cada página no sentido horário pelo ângulo dado em graus",
|
||||
|
||||
EffortMethod: "Método",
|
||||
EffortSpeed: "Velocidade",
|
||||
EffortEffort: "Esforço",
|
||||
TipEffortWebp: "Método WEBP, mais alto é melhor/mais lento (0-6, padrão 4)",
|
||||
TipEffortAvif: "Velocidade AVIF, mais alto é mais rápido/pior (0-10, padrão 10)",
|
||||
TipEffortJxl: "Esforço JXL, mais alto é melhor/mais lento (1-10, padrão 7)",
|
||||
|
||||
BtnAddFiles: "Adicionar &arquivos...",
|
||||
BtnAddDir: "Adicionar &diretório...",
|
||||
BtnRemove: "Remover",
|
||||
BtnRemoveAll: "Remover tudo",
|
||||
BtnThumbnail: "Miniatura",
|
||||
BtnCover: "Capa",
|
||||
BtnConvert: "&Converter",
|
||||
BtnCancel: "Cancelar",
|
||||
TipCancel: "Cancelar a operação em andamento (ou pressionar Esc)",
|
||||
BtnReset: "Redefinir",
|
||||
TipReset: "Restaurar todas as configurações para os valores padrão",
|
||||
BtnSave: "Salvar",
|
||||
TipSave: "Salvar as configurações atuais em um perfil",
|
||||
BtnCommand: "Comando",
|
||||
TipCommand: "Mostrar a linha de comando equivalente",
|
||||
LblProfile: "Perfil:",
|
||||
TipProfile: "Selecionar um perfil de configurações",
|
||||
|
||||
TipThumbnail: "Extrair miniaturas de capa",
|
||||
TipCover: "Extrair capas",
|
||||
TipConvert: "Converter arquivos para o formato selecionado",
|
||||
|
||||
StatusNeedFilesAndDir: "Adicionar arquivos e definir o diretório de saída",
|
||||
StatusNeedFiles: "Adicionar arquivos",
|
||||
StatusNeedOutDir: "Definir o diretório de saída",
|
||||
StatusFileOf: "Arquivo %d de %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor é o filtro de reamostragem mais rápido, sem antisserrilhamento",
|
||||
FilterBox: "Filtro Box (média dos pixels)",
|
||||
FilterLinear: "Linear é o filtro bilinear, suave e razoavelmente rápido",
|
||||
FilterMitchell: "MitchellNetravali é um filtro bicúbico suave",
|
||||
FilterCatmull: "CatmullRom é um filtro bicúbico nítido",
|
||||
FilterGaussian: "Gaussian é um filtro de desfoque que usa a função gaussiana, útil para remoção de ruído",
|
||||
FilterLanczos: "Lanczos é um filtro de reamostragem de alta qualidade, mais lento que os filtros cúbicos",
|
||||
|
||||
DlgAddFiles: "Adicionar arquivos",
|
||||
DlgAddDir: "Adicionar diretório",
|
||||
DlgOutputDir: "Diretório de saída",
|
||||
DlgOutputFile: "Arquivo de saída",
|
||||
DlgCommandLine: "Linha de comando",
|
||||
DlgSaveProfile: "Salvar perfil",
|
||||
|
||||
ParamName: "Nome: %s\n",
|
||||
MsgInvalidNameTitle: "Nome inválido",
|
||||
MsgInvalidNameBody: "O nome do perfil não deve estar vazio nem conter '.' ou ';'.",
|
||||
|
||||
NoPreview: "Sem pré-visualização",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("RUSSIAN", map[string]string{
|
||||
ColTitle: "Название",
|
||||
ColType: "Тип",
|
||||
ColSize: "Размер (МиБ)",
|
||||
|
||||
TabInput: "Ввод",
|
||||
TabOutput: "Вывод",
|
||||
TabImage: "Изображение",
|
||||
TabTransform: "Преобразование",
|
||||
|
||||
LblPage: "Страница:",
|
||||
TipPage: "Предпросмотр другой страницы выбранного комикса",
|
||||
|
||||
TglRecursive: " Обрабатывать подкаталоги",
|
||||
TipRecursive: "Рекурсивно обрабатывать подкаталоги",
|
||||
TglNoRGB: " Только изображения в оттенках серого",
|
||||
TipNoRGB: "Не преобразовывать изображения в цветовом пространстве RGB",
|
||||
TglNoCover: " Исключить обложку",
|
||||
TipNoCover: "Не преобразовывать изображение обложки",
|
||||
TglNoNonImage: " Удалить из архива файлы, не являющиеся изображениями",
|
||||
TipNoNonImage: "Удалить из архива файлы .nfo, .xml, .txt",
|
||||
TglNoConvert: " Не преобразовывать изображения",
|
||||
TipNoConvert: "Копировать изображения из архива или каталога без изменений",
|
||||
LblMinSize: "Минимальный размер (МиБ):",
|
||||
TipSize: "Обрабатывать только файлы больше минимального размера",
|
||||
LblDPI: "DPI документа:",
|
||||
TipDPI: "Разрешение для рендеринга документов (PDF, EPUB и т. д.); по умолчанию используется исходное разрешение",
|
||||
|
||||
LblOutDir: "Каталог вывода:",
|
||||
TipOutDir: "Каталог, в который записываются преобразованные файлы (обязательно)",
|
||||
BtnBrowse: "Обзор...",
|
||||
LblSuffix: "Добавить суффикс к выходному файлу:",
|
||||
TipSuffix: "Добавить суффикс к имени файла, например имяфайла_суффикс.cbz",
|
||||
LblArchive: "Формат архива:",
|
||||
TipArchive: "Выходной контейнер: ZIP (.cbz) или несжатый TAR (.cbt)",
|
||||
LblCompression: "Сжатие:",
|
||||
TipZipLevel: "Сжатие ZIP: «Хранить» отключает его, 1 — самое быстрое, 9 — наименьшее",
|
||||
TglCombine: " Объединить в один файл",
|
||||
TipCombine: "Объединить все перечисленные файлы в один архив",
|
||||
LblOutFile: "Выходной файл:",
|
||||
TipOutFile: "Имя объединённого файла (по умолчанию: первый ввод + -combined)",
|
||||
|
||||
LblFormat: "Формат:",
|
||||
TipFormat: "Выходной формат изображения для преобразованных страниц",
|
||||
LblSize: "Размер:",
|
||||
CueWidth: "ширина",
|
||||
CueHeight: "высота",
|
||||
TipWidthHeight: "Если ширина или высота не задана, соотношение сторон изображения сохраняется",
|
||||
TglFit: " Наилучшее соответствие",
|
||||
TipFit: "Наилучшее соответствие требуемой ширине и высоте",
|
||||
TglNoUpscale: " Не увеличивать",
|
||||
TipNoUpscale: "Не увеличивать изображения, которые уже меньше запрошенного размера",
|
||||
LblFilter: "Фильтр масштабирования:",
|
||||
LblQuality: "Качество: ",
|
||||
TipQuality: "Качество влияет на JPEG, WEBP, AVIF и JXL",
|
||||
LblEffort: "Усилие:",
|
||||
TipEffort: "Скорость/усилие кодировщика (WEBP, AVIF, JXL)",
|
||||
TglLossless: " Без потерь",
|
||||
TipLossless: "Сжатие без потерь (WEBP, AVIF, JXL), игнорирует качество",
|
||||
TglGrayscale: " Оттенки серого",
|
||||
TipGrayscale: "Преобразовать изображения в оттенки серого (монохромные)",
|
||||
|
||||
LblBrightness: "Яркость: ",
|
||||
TipBrightness: "Настроить яркость изображений",
|
||||
LblContrast: "Контраст: ",
|
||||
TipContrast: "Настроить контраст изображений",
|
||||
LblRotate: "Поворот:",
|
||||
TipRotate: "Поворачивать каждую страницу по часовой стрелке на заданный угол в градусах",
|
||||
|
||||
EffortMethod: "Метод",
|
||||
EffortSpeed: "Скорость",
|
||||
EffortEffort: "Усилие",
|
||||
TipEffortWebp: "Метод WEBP, выше — лучше/медленнее (0-6, по умолчанию 4)",
|
||||
TipEffortAvif: "Скорость AVIF, выше — быстрее/хуже (0-10, по умолчанию 10)",
|
||||
TipEffortJxl: "Усилие JXL, выше — лучше/медленнее (1-10, по умолчанию 7)",
|
||||
|
||||
BtnAddFiles: "Добавить &файлы...",
|
||||
BtnAddDir: "Добавить &каталог...",
|
||||
BtnRemove: "Удалить",
|
||||
BtnRemoveAll: "Удалить все",
|
||||
BtnThumbnail: "Эскиз",
|
||||
BtnCover: "Обложка",
|
||||
BtnConvert: "&Преобразовать",
|
||||
BtnCancel: "Отмена",
|
||||
TipCancel: "Отменить выполняемую операцию (или нажать Esc)",
|
||||
BtnReset: "Сбросить",
|
||||
TipReset: "Восстановить все настройки до значений по умолчанию",
|
||||
BtnSave: "Сохранить",
|
||||
TipSave: "Сохранить текущие настройки в профиль",
|
||||
BtnCommand: "Команда",
|
||||
TipCommand: "Показать эквивалентную командную строку",
|
||||
LblProfile: "Профиль:",
|
||||
TipProfile: "Выбрать профиль настроек",
|
||||
|
||||
TipThumbnail: "Извлечь эскизы обложек",
|
||||
TipCover: "Извлечь обложки",
|
||||
TipConvert: "Преобразовать файлы в выбранный формат",
|
||||
|
||||
StatusNeedFilesAndDir: "Добавьте файлы и задайте каталог вывода",
|
||||
StatusNeedFiles: "Добавьте файлы",
|
||||
StatusNeedOutDir: "Задайте каталог вывода",
|
||||
StatusFileOf: "Файл %d из %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor — самый быстрый фильтр передискретизации, без сглаживания",
|
||||
FilterBox: "Фильтр Box (усреднение пикселей)",
|
||||
FilterLinear: "Linear — билинейный фильтр, гладкий и достаточно быстрый",
|
||||
FilterMitchell: "MitchellNetravali — гладкий бикубический фильтр",
|
||||
FilterCatmull: "CatmullRom — резкий бикубический фильтр",
|
||||
FilterGaussian: "Gaussian — размывающий фильтр на основе функции Гаусса, полезен для удаления шума",
|
||||
FilterLanczos: "Lanczos — высококачественный фильтр передискретизации, медленнее кубических фильтров",
|
||||
|
||||
DlgAddFiles: "Добавить файлы",
|
||||
DlgAddDir: "Добавить каталог",
|
||||
DlgOutputDir: "Каталог вывода",
|
||||
DlgOutputFile: "Выходной файл",
|
||||
DlgCommandLine: "Командная строка",
|
||||
DlgSaveProfile: "Сохранить профиль",
|
||||
|
||||
ParamName: "Имя: %s\n",
|
||||
MsgInvalidNameTitle: "Недопустимое имя",
|
||||
MsgInvalidNameBody: "Имя профиля не должно быть пустым или содержать «.» или «;».",
|
||||
|
||||
NoPreview: "Нет предпросмотра",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package i18n
|
||||
|
||||
func init() {
|
||||
register("CHINESE", map[string]string{
|
||||
ColTitle: "标题",
|
||||
ColType: "类型",
|
||||
ColSize: "大小 (MiB)",
|
||||
|
||||
TabInput: "输入",
|
||||
TabOutput: "输出",
|
||||
TabImage: "图像",
|
||||
TabTransform: "变换",
|
||||
|
||||
LblPage: "页面:",
|
||||
TipPage: "预览所选漫画的其他页面",
|
||||
|
||||
TglRecursive: " 递归子目录",
|
||||
TipRecursive: "递归处理子目录",
|
||||
TglNoRGB: " 仅灰度图像",
|
||||
TipNoRGB: "不转换具有 RGB 色彩空间的图像",
|
||||
TglNoCover: " 排除封面",
|
||||
TipNoCover: "不转换封面图像",
|
||||
TglNoNonImage: " 从存档中移除非图像文件",
|
||||
TipNoNonImage: "从存档中移除 .nfo、.xml、.txt 文件",
|
||||
TglNoConvert: " 不变换或转换图像",
|
||||
TipNoConvert: "从存档或目录复制图像而不做修改",
|
||||
LblMinSize: "最小大小 (MiB):",
|
||||
TipSize: "仅处理大于最小大小的文件",
|
||||
LblDPI: "文档 DPI:",
|
||||
TipDPI: "渲染文档的分辨率(PDF、EPUB 等);默认使用原始分辨率",
|
||||
|
||||
LblOutDir: "输出目录:",
|
||||
TipOutDir: "写入转换后文件的目录(必填)",
|
||||
BtnBrowse: "浏览...",
|
||||
LblSuffix: "为输出文件添加后缀:",
|
||||
TipSuffix: "为文件名添加后缀,例如 filename_suffix.cbz",
|
||||
LblArchive: "存档格式:",
|
||||
TipArchive: "输出容器:ZIP (.cbz) 或未压缩的 TAR (.cbt)",
|
||||
LblCompression: "压缩:",
|
||||
TipZipLevel: "ZIP 压缩:存储将其禁用,1 最快,9 最小",
|
||||
TglCombine: " 合并为单个文件",
|
||||
TipCombine: "将所有列出的文件合并为一个存档",
|
||||
LblOutFile: "输出文件:",
|
||||
TipOutFile: "合并文件名(默认:第一个输入 + -combined)",
|
||||
|
||||
LblFormat: "格式:",
|
||||
TipFormat: "转换页面的输出图像格式",
|
||||
LblSize: "大小:",
|
||||
CueWidth: "宽度",
|
||||
CueHeight: "高度",
|
||||
TipWidthHeight: "如果未设置宽度或高度,则保留图像的纵横比",
|
||||
TglFit: " 最佳适应",
|
||||
TipFit: "最佳适应所需的宽度和高度",
|
||||
TglNoUpscale: " 不放大",
|
||||
TipNoUpscale: "不放大已经小于请求大小的图像",
|
||||
LblFilter: "缩放滤镜:",
|
||||
LblQuality: "质量:",
|
||||
TipQuality: "质量影响 JPEG、WEBP、AVIF 和 JXL",
|
||||
LblEffort: "强度:",
|
||||
TipEffort: "编码器速度/强度(WEBP、AVIF、JXL)",
|
||||
TglLossless: " 无损",
|
||||
TipLossless: "无损压缩(WEBP、AVIF、JXL),忽略质量",
|
||||
TglGrayscale: " 灰度",
|
||||
TipGrayscale: "将图像转换为灰度(单色)",
|
||||
|
||||
LblBrightness: "亮度:",
|
||||
TipBrightness: "调整图像的亮度",
|
||||
LblContrast: "对比度:",
|
||||
TipContrast: "调整图像的对比度",
|
||||
LblRotate: "旋转:",
|
||||
TipRotate: "按给定角度(度)顺时针旋转每一页",
|
||||
|
||||
EffortMethod: "方法",
|
||||
EffortSpeed: "速度",
|
||||
EffortEffort: "强度",
|
||||
TipEffortWebp: "WEBP 方法,越高越好/越慢(0-6,默认 4)",
|
||||
TipEffortAvif: "AVIF 速度,越高越快/越差(0-10,默认 10)",
|
||||
TipEffortJxl: "JXL 强度,越高越好/越慢(1-10,默认 7)",
|
||||
|
||||
BtnAddFiles: "添加文件(&F)...",
|
||||
BtnAddDir: "添加目录(&D)...",
|
||||
BtnRemove: "移除",
|
||||
BtnRemoveAll: "全部移除",
|
||||
BtnThumbnail: "缩略图",
|
||||
BtnCover: "封面",
|
||||
BtnConvert: "转换(&C)",
|
||||
BtnCancel: "取消",
|
||||
TipCancel: "取消正在运行的操作(或按 Esc)",
|
||||
BtnReset: "重置",
|
||||
TipReset: "将所有设置恢复为默认值",
|
||||
BtnSave: "保存",
|
||||
TipSave: "将当前设置保存到配置文件",
|
||||
BtnCommand: "命令",
|
||||
TipCommand: "显示等效的命令行",
|
||||
LblProfile: "配置文件:",
|
||||
TipProfile: "选择设置配置文件",
|
||||
|
||||
TipThumbnail: "提取封面缩略图",
|
||||
TipCover: "提取封面",
|
||||
TipConvert: "将文件转换为所选格式",
|
||||
|
||||
StatusNeedFilesAndDir: "添加文件并设置输出目录",
|
||||
StatusNeedFiles: "添加文件",
|
||||
StatusNeedOutDir: "设置输出目录",
|
||||
StatusFileOf: "文件 %d / %d",
|
||||
|
||||
FilterNearest: "NearestNeighbor 是最快的重采样滤镜,无抗锯齿",
|
||||
FilterBox: "Box 滤镜(像素平均)",
|
||||
FilterLinear: "Linear 是双线性滤镜,平滑且相当快",
|
||||
FilterMitchell: "MitchellNetravali 是平滑的双三次滤镜",
|
||||
FilterCatmull: "CatmullRom 是锐利的双三次滤镜",
|
||||
FilterGaussian: "Gaussian 是使用高斯函数的模糊滤镜,适用于降噪",
|
||||
FilterLanczos: "Lanczos 是高质量的重采样滤镜,比三次滤镜慢",
|
||||
|
||||
DlgAddFiles: "添加文件",
|
||||
DlgAddDir: "添加目录",
|
||||
DlgOutputDir: "输出目录",
|
||||
DlgOutputFile: "输出文件",
|
||||
DlgCommandLine: "命令行",
|
||||
DlgSaveProfile: "保存配置文件",
|
||||
|
||||
ParamName: "名称:%s\n",
|
||||
MsgInvalidNameTitle: "无效名称",
|
||||
MsgInvalidNameBody: "配置文件名称不能为空,也不能包含 '.' 或 ';'。",
|
||||
|
||||
NoPreview: "无预览",
|
||||
})
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
//go:build !portal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
func fileDlg(title string, multiple, directory bool) ([]string, error) {
|
||||
ret := make([]string, 0)
|
||||
|
||||
dlg := iup.FileDlg()
|
||||
defer dlg.Destroy()
|
||||
|
||||
if !directory {
|
||||
mf := "YES"
|
||||
if !multiple {
|
||||
mf = "NO"
|
||||
}
|
||||
|
||||
dlg.SetAttributes(map[string]string{
|
||||
"DIALOGTYPE": "OPEN",
|
||||
"MULTIPLEFILES": mf,
|
||||
"EXTFILTER": "Comic Files|*.rar;*.zip;*.7z;*.tar;*.cbr;*.cbz;*.cb7;*.cbt;*.pdf;*.epub;*.mobi;*.xps|",
|
||||
"FILTER": "*.cb*", // for Motif
|
||||
"TITLE": title,
|
||||
})
|
||||
} else {
|
||||
dlg.SetAttributes(map[string]string{
|
||||
"DIALOGTYPE": "DIR",
|
||||
"TITLE": title,
|
||||
})
|
||||
}
|
||||
|
||||
iup.Popup(dlg, iup.CENTERPARENT, iup.CENTERPARENT)
|
||||
|
||||
if dlg.GetInt("STATUS") == 0 {
|
||||
if !directory {
|
||||
value := dlg.GetAttribute("VALUE")
|
||||
sp := strings.Split(value, "|")
|
||||
|
||||
if strings.ToLower(iup.GetGlobal("DRIVER")) == "cocoa" {
|
||||
for _, file := range sp {
|
||||
ret = append(ret, file)
|
||||
}
|
||||
} else {
|
||||
if len(sp) > 1 {
|
||||
for _, file := range sp[1 : len(sp)-1] {
|
||||
ret = append(ret, filepath.Join(sp[0], file))
|
||||
}
|
||||
} else {
|
||||
ret = append(ret, value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value := dlg.GetAttribute("VALUE")
|
||||
ret = append(ret, value)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
//go:build portal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
func fileDlg(title string, multiple, directory bool) ([]string, error) {
|
||||
ret := make([]string, 0)
|
||||
|
||||
conn, err := dbus.ConnectSessionBus()
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dest := "org.freedesktop.portal.Desktop"
|
||||
path := "/org/freedesktop/portal/desktop"
|
||||
resp := "org.freedesktop.portal.Request.Response"
|
||||
|
||||
if err = conn.AddMatchSignal(
|
||||
dbus.WithMatchInterface(dest),
|
||||
dbus.WithMatchObjectPath(dbus.ObjectPath(path)),
|
||||
dbus.WithMatchSender(conn.Names()[0]),
|
||||
); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
c := make(chan *dbus.Signal, 10)
|
||||
conn.Signal(c)
|
||||
|
||||
type Item struct {
|
||||
Index uint32
|
||||
Filter string
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
Title string
|
||||
Filters []Item
|
||||
}
|
||||
|
||||
filters := []Filter{
|
||||
{
|
||||
"Comic Files",
|
||||
[]Item{
|
||||
Item{0, "*.rar"},
|
||||
Item{0, "*.zip"},
|
||||
Item{0, "*.7z"},
|
||||
Item{0, "*.tar"},
|
||||
Item{0, "*.cbr"},
|
||||
Item{0, "*.cbz"},
|
||||
Item{0, "*.cb7"},
|
||||
Item{0, "*.cbt"},
|
||||
Item{0, "*.pdf"},
|
||||
Item{0, "*.epub"},
|
||||
Item{0, "*.mobi"},
|
||||
Item{0, "*.xps"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
opts := map[string]any{
|
||||
"multiple": multiple,
|
||||
"directory": directory,
|
||||
}
|
||||
|
||||
if !directory {
|
||||
opts["filters"] = filters
|
||||
}
|
||||
|
||||
obj := conn.Object(dest, dbus.ObjectPath(path))
|
||||
call := obj.Call("org.freedesktop.portal.FileChooser.OpenFile", 0, "", title, opts)
|
||||
if call.Err != nil {
|
||||
return ret, call.Err
|
||||
}
|
||||
|
||||
for v := range c {
|
||||
if v.Name != resp {
|
||||
continue
|
||||
}
|
||||
|
||||
status := v.Body[0].(uint32)
|
||||
|
||||
if status == 0 {
|
||||
m := v.Body[1].(map[string]dbus.Variant)
|
||||
uris := m["uris"].Value().([]string)
|
||||
|
||||
for _, uri := range uris {
|
||||
u, err := url.ParseRequestURI(uri)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
ret = append(ret, u.Path)
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GLIBC_x86_64="/usr/x86_64-pc-linux-gnu-static"
|
||||
MINGW_x86_64="/usr/x86_64-w64-mingw32"
|
||||
MACOS_x86_64="/usr/x86_64-apple-darwin"
|
||||
MACOS_aarch64="/usr/aarch64-apple-darwin"
|
||||
|
||||
VERSION="$(git --git-dir ../../.git describe --tags --abbrev=0 2>/dev/null || echo '1.0.0')"
|
||||
|
||||
BUILDDIR="cbconvert-gui-${VERSION}"
|
||||
|
||||
mkdir -p "${BUILDDIR}"
|
||||
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 -x -o "${BUILDDIR}"/cbconvert-gui -ldflags "-s -w -X main.appVersion=${VERSION}" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && tar -czf "${BUILDDIR}-linux-x86_64.tar.gz" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
mkdir -p "${BUILDDIR}"
|
||||
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 -lxcb -lICE -lXdmcp -lXau -lSM" \
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig motif' -v -o "${BUILDDIR}"/cbconvert-gui-motif -ldflags "-s -w -linkmode external -X main.appVersion=${VERSION} '-extldflags=-static'" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && tar -czf "${BUILDDIR}-linux-x86_64-motif.tar.gz" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
mkdir -p "${BUILDDIR}"
|
||||
CC=x86_64-w64-mingw32-gcc \
|
||||
PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MINGW_x86_64/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MINGW_x86_64/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MINGW_x86_64/usr/include" \
|
||||
CGO_LDFLAGS="-L$MINGW_x86_64/usr/lib" \
|
||||
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o "${BUILDDIR}"/cbconvert-gui.exe -ldflags "-s -w -X main.appVersion=${VERSION} -H=windowsgui '-extldflags=-static -Wl,--allow-multiple-definition'" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && zip -rq "${BUILDDIR}-windows-x86_64.zip" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1
|
||||
mkdir -p "${BUILDDIR}"
|
||||
cp -r dist/macos/CBconvert.app "${BUILDDIR}"
|
||||
PATH=${PATH}:${MACOS_x86_64}/bin \
|
||||
CC=x86_64-apple-darwin21.1-clang \
|
||||
PKG_CONFIG="x86_64-apple-darwin21.1-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MACOS_x86_64/usr/include -I$MACOS_x86_64/macports/pkgs/opt/local/include" \
|
||||
CGO_LDFLAGS="-L$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib -L$MACOS_x86_64/macports/pkgs/opt/local/lib -mmacosx-version-min=10.15" \
|
||||
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o "${BUILDDIR}"/CBconvert.app/Contents/MacOS/cbconvert-gui -ldflags "-linkmode external -s -w -X main.appVersion=${VERSION}" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && zip -rq "${BUILDDIR}-darwin-x86_64.zip" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1
|
||||
mkdir -p "${BUILDDIR}"
|
||||
cp -r dist/macos/CBconvert.app "${BUILDDIR}"
|
||||
PATH=${PATH}:${MACOS_aarch64}/bin \
|
||||
CC=aarch64-apple-darwin21.1-clang \
|
||||
PKG_CONFIG="aarch64-apple-darwin21.1-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MACOS_aarch64/usr/include -I$MACOS_aarch64/macports/pkgs/opt/local/include" \
|
||||
CGO_LDFLAGS="-L$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib -L$MACOS_aarch64/macports/pkgs/opt/local/lib -mmacosx-version-min=10.15" \
|
||||
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o "${BUILDDIR}"/CBconvert.app/Contents/MacOS/cbconvert-gui -ldflags "-linkmode external -s -w -X main.appVersion=${VERSION}" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && zip -rq "${BUILDDIR}-darwin-aarch64.zip" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
@@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/cbconvert"
|
||||
"github.com/gen2brain/cbconvert/cmd/cbconvert-gui/i18n"
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
func options() cbconvert.Options {
|
||||
var opts cbconvert.Options
|
||||
opts.Recursive = iup.GetHandle("Recursive").GetAttribute("VALUE") == "ON"
|
||||
opts.NoRGB = iup.GetHandle("NoRGB").GetAttribute("VALUE") == "ON"
|
||||
opts.NoCover = iup.GetHandle("NoCover").GetAttribute("VALUE") == "ON"
|
||||
opts.Size = iup.GetHandle("Size").GetInt("VALUE")
|
||||
opts.OutDir = iup.GetHandle("OutDir").GetAttribute("VALUE")
|
||||
opts.Suffix = iup.GetHandle("Suffix").GetAttribute("VALUE")
|
||||
opts.NoConvert = iup.GetHandle("NoConvert").GetAttribute("VALUE") == "ON"
|
||||
opts.NoNonImage = iup.GetHandle("NoNonImage").GetAttribute("VALUE") == "ON"
|
||||
opts.Archive = strings.ToLower(iup.GetHandle("Archive").GetAttribute("VALUESTRING"))
|
||||
opts.ZipLevel = zipLevel(iup.GetHandle("ZipLevel").GetAttribute("VALUESTRING"))
|
||||
opts.Format = strings.ToLower(iup.GetHandle("Format").GetAttribute("VALUESTRING"))
|
||||
opts.Width = iup.GetHandle("Width").GetInt("VALUE")
|
||||
opts.Height = iup.GetHandle("Height").GetInt("VALUE")
|
||||
opts.DPI = dpiValue(iup.GetHandle("DPI").GetAttribute("VALUE"))
|
||||
opts.Fit = iup.GetHandle("Fit").GetAttribute("VALUE") == "ON"
|
||||
opts.NoUpscale = iup.GetHandle("NoUpscale").GetAttribute("VALUE") == "ON"
|
||||
opts.Filter = iup.GetHandle("Filter").GetInt("VALUE") - 1
|
||||
opts.Quality = iup.GetHandle("Quality").GetInt("VALUE")
|
||||
switch opts.Format {
|
||||
case "webp", "avif", "jxl":
|
||||
opts.Effort = iup.GetHandle("Effort").GetInt("VALUE")
|
||||
default:
|
||||
opts.Effort = -1
|
||||
}
|
||||
opts.Lossless = iup.GetHandle("Lossless").GetAttribute("VALUE") == "ON"
|
||||
opts.Combine = iup.GetHandle("Combine").GetAttribute("VALUE") == "ON"
|
||||
if opts.Combine {
|
||||
opts.OutFile = iup.GetHandle("OutFile").GetAttribute("VALUE")
|
||||
}
|
||||
opts.Grayscale = iup.GetHandle("Grayscale").GetAttribute("VALUE") == "ON"
|
||||
opts.Brightness = iup.GetHandle("Brightness").GetInt("VALUE")
|
||||
opts.Contrast = iup.GetHandle("Contrast").GetInt("VALUE")
|
||||
opts.Rotate = iup.GetHandle("Rotate").GetInt("VALUESTRING")
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
// shellArg quotes a command-line argument that contains whitespace.
|
||||
func shellArg(s string) string {
|
||||
if strings.ContainsAny(s, " \t") {
|
||||
return `"` + strings.ReplaceAll(s, `"`, `\"`) + `"`
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func commandLine() string {
|
||||
parts := append([]string{"cbconvert", "convert"}, options().Args()...)
|
||||
for _, file := range files {
|
||||
parts = append(parts, file.Path)
|
||||
}
|
||||
|
||||
for i, p := range parts {
|
||||
parts[i] = shellArg(p)
|
||||
}
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
func onCommand(iup.Ihandle) int {
|
||||
iup.GetText(i18n.Str(i18n.DlgCommandLine), commandLine(), -1)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
// zipLevel maps the compression dropdown selection to Options.ZipLevel.
|
||||
func zipLevel(value string) int {
|
||||
switch value {
|
||||
case "Default":
|
||||
return -1
|
||||
case "Store (none)":
|
||||
return 0
|
||||
default:
|
||||
level, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
}
|
||||
|
||||
func dpiValue(value string) int {
|
||||
dpi, err := strconv.Atoi(strings.TrimSpace(value))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return dpi
|
||||
}
|
||||
@@ -4,10 +4,7 @@ Windows
|
||||
<img src="windows-01.jpg" width="700" title="Windows" alt="Windows" />
|
||||
|
||||
Linux
|
||||
<img src="linux-02.jpg" width="700" title="Linux" alt="Linux" />
|
||||
<img src="linux-01.jpg" width="700" title="Linux" alt="Linux" />
|
||||
|
||||
macOS
|
||||
<img src="macos-01.jpg" width="700" title="macOS" alt="macOS" />
|
||||
|
||||
Linux Motif
|
||||
<img src="motif-01.jpg" width="700" title="Linux Motif" alt="Linux Motif" />
|
||||
<img src="macos-01.jpg" width="700" title="macOS" alt="macOS" />
|
||||
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 280 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 185 KiB |
@@ -0,0 +1,252 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/cbconvert/cmd/cbconvert-gui/i18n"
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
const (
|
||||
pathsGroup = "Paths"
|
||||
profilesGroup = "Profiles"
|
||||
|
||||
inputDirKey = "InputDir"
|
||||
outputDirKey = "OutputDir"
|
||||
)
|
||||
|
||||
type settingKind int
|
||||
|
||||
const (
|
||||
kindBool settingKind = iota
|
||||
kindInt
|
||||
kindStr
|
||||
)
|
||||
|
||||
type setting struct {
|
||||
handle string
|
||||
kind settingKind
|
||||
def string
|
||||
}
|
||||
|
||||
var settings = []setting{
|
||||
{"Recursive", kindBool, "OFF"},
|
||||
{"NoRGB", kindBool, "OFF"},
|
||||
{"NoCover", kindBool, "OFF"},
|
||||
{"NoConvert", kindBool, "OFF"},
|
||||
{"NoNonImage", kindBool, "OFF"},
|
||||
{"Combine", kindBool, "OFF"},
|
||||
{"Fit", kindBool, "OFF"},
|
||||
{"NoUpscale", kindBool, "OFF"},
|
||||
{"Lossless", kindBool, "OFF"},
|
||||
{"Grayscale", kindBool, "OFF"},
|
||||
{"OutDir", kindStr, ""},
|
||||
{"Suffix", kindStr, ""},
|
||||
{"Width", kindStr, ""},
|
||||
{"Height", kindStr, ""},
|
||||
{"DPI", kindStr, "Default"},
|
||||
{"Size", kindInt, "0"},
|
||||
{"Quality", kindInt, "75"},
|
||||
{"Effort", kindInt, "0"},
|
||||
{"Brightness", kindInt, "0"},
|
||||
{"Contrast", kindInt, "0"},
|
||||
{"Format", kindInt, "1"},
|
||||
{"Archive", kindInt, "1"},
|
||||
{"ZipLevel", kindInt, "1"},
|
||||
{"Filter", kindInt, "3"},
|
||||
{"Rotate", kindInt, "1"},
|
||||
}
|
||||
|
||||
func profileGroup(name string) string {
|
||||
return "Profile:" + name
|
||||
}
|
||||
|
||||
func profileNames() []string {
|
||||
s := iup.ConfigGetVariableStr(config, profilesGroup, "Names")
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return strings.Split(s, ";")
|
||||
}
|
||||
|
||||
func currentProfile() string {
|
||||
return iup.ConfigGetVariableStrDef(config, profilesGroup, "Current", "Default")
|
||||
}
|
||||
|
||||
func setStartDir(dlg iup.Ihandle, key string) {
|
||||
if dir := iup.ConfigGetVariableStr(config, pathsGroup, key); dir != "" {
|
||||
dlg.SetAttribute("DIRECTORY", dir)
|
||||
}
|
||||
}
|
||||
|
||||
func rememberDir(dlg iup.Ihandle, key string) {
|
||||
dir := dlg.GetAttribute("DIRECTORY")
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
|
||||
iup.ConfigSetVariableStr(config, pathsGroup, key, dir)
|
||||
iup.ConfigSave(config)
|
||||
}
|
||||
|
||||
func settingsSave(group string) {
|
||||
for _, s := range settings {
|
||||
h := iup.GetHandle(s.handle)
|
||||
switch s.kind {
|
||||
case kindBool:
|
||||
v := 0
|
||||
if h.GetAttribute("VALUE") == "ON" {
|
||||
v = 1
|
||||
}
|
||||
iup.ConfigSetVariableInt(config, group, s.handle, v)
|
||||
case kindInt:
|
||||
iup.ConfigSetVariableInt(config, group, s.handle, h.GetInt("VALUE"))
|
||||
case kindStr:
|
||||
iup.ConfigSetVariableStr(config, group, s.handle, h.GetAttribute("VALUE"))
|
||||
}
|
||||
}
|
||||
|
||||
iup.ConfigSave(config)
|
||||
}
|
||||
|
||||
// settingsApply sets every control from the given profile group, or from defaults when a group is empty.
|
||||
func settingsApply(group string) {
|
||||
for _, s := range settings {
|
||||
h := iup.GetHandle(s.handle)
|
||||
switch s.kind {
|
||||
case kindBool:
|
||||
def := 0
|
||||
if s.def == "ON" {
|
||||
def = 1
|
||||
}
|
||||
v := def
|
||||
if group != "" {
|
||||
v = iup.ConfigGetVariableIntDef(config, group, s.handle, def)
|
||||
}
|
||||
if v != 0 {
|
||||
h.SetAttribute("VALUE", "ON")
|
||||
} else {
|
||||
h.SetAttribute("VALUE", "OFF")
|
||||
}
|
||||
case kindInt:
|
||||
def, _ := strconv.Atoi(s.def)
|
||||
v := def
|
||||
if group != "" {
|
||||
v = iup.ConfigGetVariableIntDef(config, group, s.handle, def)
|
||||
}
|
||||
h.SetAttribute("VALUE", strconv.Itoa(v))
|
||||
case kindStr:
|
||||
v := s.def
|
||||
if group != "" {
|
||||
v = iup.ConfigGetVariableStrDef(config, group, s.handle, s.def)
|
||||
}
|
||||
h.SetAttribute("VALUE", v)
|
||||
}
|
||||
}
|
||||
|
||||
userLossless = iup.GetHandle("Lossless").GetAttribute("VALUE") == "ON"
|
||||
|
||||
syncLabels()
|
||||
setActive()
|
||||
previewPost()
|
||||
}
|
||||
|
||||
// syncLabels mirrors slider values into their value labels and retunes the effort slider for the current format.
|
||||
func syncLabels() {
|
||||
iup.GetHandle("LabelQuality").SetAttribute("TITLE", iup.GetHandle("Quality").GetInt("VALUE"))
|
||||
iup.GetHandle("LabelBrightness").SetAttribute("TITLE", iup.GetHandle("Brightness").GetInt("VALUE"))
|
||||
iup.GetHandle("LabelContrast").SetAttribute("TITLE", iup.GetHandle("Contrast").GetInt("VALUE"))
|
||||
|
||||
format := strings.ToLower(iup.GetHandle("Format").GetAttribute("VALUESTRING"))
|
||||
eff := iup.GetHandle("Effort").GetInt("VALUE")
|
||||
setEffort(format)
|
||||
switch format {
|
||||
case "webp", "avif", "jxl":
|
||||
val := iup.GetHandle("Effort")
|
||||
val.SetAttribute("VALUE", strconv.Itoa(eff))
|
||||
iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", val.GetAttribute("EFFORTNAME"), eff))
|
||||
}
|
||||
|
||||
iup.Refresh(iup.GetHandle("Tabs"))
|
||||
}
|
||||
|
||||
func fillProfileList() {
|
||||
list := iup.GetHandle("Profile")
|
||||
list.SetAttribute("REMOVEITEM", "ALL")
|
||||
|
||||
cur := currentProfile()
|
||||
sel := 1
|
||||
for i, n := range profileNames() {
|
||||
list.SetAttribute(strconv.Itoa(i+1), n)
|
||||
if n == cur {
|
||||
sel = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
list.SetAttribute("VALUE", strconv.Itoa(sel))
|
||||
}
|
||||
|
||||
// profilesInit loads the current profile on startup, creating a default one on the first run.
|
||||
func profilesInit() {
|
||||
if len(profileNames()) == 0 {
|
||||
iup.ConfigSetVariableStr(config, profilesGroup, "Names", "Default")
|
||||
iup.ConfigSetVariableStr(config, profilesGroup, "Current", "Default")
|
||||
settingsSave(profileGroup("Default"))
|
||||
}
|
||||
|
||||
fillProfileList()
|
||||
settingsApply(profileGroup(currentProfile()))
|
||||
}
|
||||
|
||||
func onProfileSelect(ih iup.Ihandle) int {
|
||||
name := ih.GetAttribute("VALUESTRING")
|
||||
if name == "" {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
iup.ConfigSetVariableStr(config, profilesGroup, "Current", name)
|
||||
iup.ConfigSave(config)
|
||||
|
||||
settingsApply(profileGroup(name))
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onSave(iup.Ihandle) int {
|
||||
name := currentProfile()
|
||||
if iup.GetParam(i18n.Str(i18n.DlgSaveProfile), nil, i18n.Str(i18n.ParamName), &name) != 1 {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" || strings.ContainsAny(name, ".;") {
|
||||
iup.Message(i18n.Str(i18n.MsgInvalidNameTitle), i18n.Str(i18n.MsgInvalidNameBody))
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
settingsSave(profileGroup(name))
|
||||
|
||||
names := profileNames()
|
||||
if !slices.Contains(names, name) {
|
||||
names = append(names, name)
|
||||
iup.ConfigSetVariableStr(config, profilesGroup, "Names", strings.Join(names, ";"))
|
||||
}
|
||||
|
||||
iup.ConfigSetVariableStr(config, profilesGroup, "Current", name)
|
||||
iup.ConfigSave(config)
|
||||
|
||||
fillProfileList()
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
func onReset(iup.Ihandle) int {
|
||||
settingsApply("")
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/cbconvert/cmd/cbconvert-gui/i18n"
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
// jxlLosslessBuild reports whether wasm2go and nodynamic leave zune-jpegxl (lossless-only) as the only jxl encoder.
|
||||
var jxlLosslessBuild = func() bool {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
var wasm2go, nodynamic bool
|
||||
for _, kv := range info.Settings {
|
||||
if kv.Key != "-tags" {
|
||||
continue
|
||||
}
|
||||
for _, t := range strings.Split(kv.Value, ",") {
|
||||
switch t {
|
||||
case "wasm2go":
|
||||
wasm2go = true
|
||||
case "nodynamic":
|
||||
nodynamic = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return wasm2go && nodynamic
|
||||
}()
|
||||
|
||||
// userLossless is the user's Lossless preference, tracked separately because a jxl
|
||||
// wasm2go build force-sets the widget on.
|
||||
var userLossless bool
|
||||
|
||||
func setActive() {
|
||||
if busy {
|
||||
return
|
||||
}
|
||||
|
||||
opts := options()
|
||||
count := iup.GetHandle("Table").GetInt("NUMLIN")
|
||||
|
||||
if count > 0 && index != -1 {
|
||||
iup.GetHandle("PageBox").SetAttribute("VISIBLE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("PageBox").SetAttribute("VISIBLE", "NO")
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
iup.GetHandle("Remove").SetAttribute("ACTIVE", "NO")
|
||||
iup.GetHandle("RemoveAll").SetAttribute("ACTIVE", "NO")
|
||||
|
||||
iup.GetHandle("Preview").SetAttribute("IMAGE", "logo")
|
||||
iup.GetHandle("PreviewInfo").SetAttribute("TITLE", "")
|
||||
} else {
|
||||
if index != -1 {
|
||||
iup.GetHandle("Remove").SetAttribute("ACTIVE", "YES")
|
||||
}
|
||||
iup.GetHandle("RemoveAll").SetAttribute("ACTIVE", "YES")
|
||||
}
|
||||
|
||||
active := "YES"
|
||||
var tip string
|
||||
switch {
|
||||
case count == 0 && opts.OutDir == "":
|
||||
active, tip = "NO", i18n.Lng(i18n.StatusNeedFilesAndDir)
|
||||
case count == 0:
|
||||
active, tip = "NO", i18n.Lng(i18n.StatusNeedFiles)
|
||||
case opts.OutDir == "":
|
||||
active, tip = "NO", i18n.Lng(i18n.StatusNeedOutDir)
|
||||
}
|
||||
|
||||
enabledTip := map[string]string{
|
||||
"Thumbnail": i18n.Lng(i18n.TipThumbnail),
|
||||
"Cover": i18n.Lng(i18n.TipCover),
|
||||
"Convert": i18n.Lng(i18n.TipConvert),
|
||||
}
|
||||
|
||||
for _, h := range []string{"Thumbnail", "Cover", "Convert"} {
|
||||
b := iup.GetHandle(h)
|
||||
b.SetAttribute("ACTIVE", active)
|
||||
if active == "YES" {
|
||||
b.SetAttribute("TIP", enabledTip[h])
|
||||
} else {
|
||||
b.SetAttribute("TIP", tip)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.NoConvert {
|
||||
iup.GetHandle("VboxImage").SetAttribute("ACTIVE", "NO")
|
||||
iup.GetHandle("VboxTransform").SetAttribute("ACTIVE", "NO")
|
||||
} else {
|
||||
iup.GetHandle("VboxImage").SetAttribute("ACTIVE", "YES")
|
||||
iup.GetHandle("VboxTransform").SetAttribute("ACTIVE", "YES")
|
||||
}
|
||||
|
||||
canLossless := opts.Format == "webp" || opts.Format == "avif" || opts.Format == "jxl"
|
||||
jxlLossless := jxlLosslessBuild && opts.Format == "jxl"
|
||||
|
||||
// jxl wasm2go forces lossless on; otherwise show the user's preference so it doesn't stay stuck on.
|
||||
losslessVal := "OFF"
|
||||
if jxlLossless || userLossless {
|
||||
losslessVal = "ON"
|
||||
}
|
||||
iup.GetHandle("Lossless").SetAttribute("VALUE", losslessVal)
|
||||
losslessOn := jxlLossless || (canLossless && userLossless)
|
||||
|
||||
if (opts.Format == "jpeg" || canLossless) && !opts.NoConvert && !losslessOn {
|
||||
iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("VboxQuality").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
|
||||
if canLossless && !opts.NoConvert {
|
||||
iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "YES")
|
||||
iup.GetHandle("Lossless").SetAttribute("ACTIVE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "NO")
|
||||
iup.GetHandle("Lossless").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
|
||||
if jxlLossless {
|
||||
iup.GetHandle("Lossless").SetAttribute("ACTIVE", "NO")
|
||||
iup.GetHandle("VboxEffort").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
|
||||
if opts.Width != 0 && opts.Height != 0 && !opts.NoConvert {
|
||||
iup.GetHandle("Fit").SetAttribute("ACTIVE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("Fit").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
|
||||
if opts.Combine {
|
||||
iup.GetHandle("VboxOutFile").SetAttribute("ACTIVE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("VboxOutFile").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
|
||||
if opts.Archive == "zip" {
|
||||
iup.GetHandle("VboxZipLevel").SetAttribute("ACTIVE", "YES")
|
||||
} else {
|
||||
iup.GetHandle("VboxZipLevel").SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
}
|
||||
|
||||
// setBusy locks the UI while an operation runs and turns Convert into a Cancel button.
|
||||
func setBusy(on bool) {
|
||||
busy = on
|
||||
|
||||
// Controls not governed by setActive; setActive owns the rest.
|
||||
always := "YES"
|
||||
if on {
|
||||
always = "NO"
|
||||
}
|
||||
for _, h := range []string{"AddFiles", "AddDir", "Profile", "Reset", "Save", "Command", "Tabs", "Table"} {
|
||||
iup.GetHandle(h).SetAttribute("ACTIVE", always)
|
||||
}
|
||||
|
||||
convert := iup.GetHandle("Convert")
|
||||
if on {
|
||||
for _, h := range []string{"Remove", "RemoveAll", "Thumbnail", "Cover"} {
|
||||
iup.GetHandle(h).SetAttribute("ACTIVE", "NO")
|
||||
}
|
||||
convert.SetAttribute("ACTIVE", "YES")
|
||||
convert.SetAttribute("TITLE", i18n.Lng(i18n.BtnCancel))
|
||||
convert.SetAttribute("TIP", i18n.Lng(i18n.TipCancel))
|
||||
} else {
|
||||
activeConv = nil
|
||||
convert.SetAttribute("TITLE", i18n.Lng(i18n.BtnConvert))
|
||||
convert.SetAttribute("TIP", i18n.Lng(i18n.TipConvert))
|
||||
setActive() // restores the conditional buttons and option boxes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,798 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image/gif"
|
||||
"math"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gen2brain/cbconvert"
|
||||
"github.com/gen2brain/cbconvert/cmd/cbconvert-gui/i18n"
|
||||
"github.com/gen2brain/iup-go/iup"
|
||||
)
|
||||
|
||||
func setEffort(format string) {
|
||||
val := iup.GetHandle("Effort")
|
||||
|
||||
var name string
|
||||
|
||||
switch format {
|
||||
case "webp":
|
||||
val.SetAttributes("MIN=0, MAX=6, SHOWTICKS=7, VALUE=4")
|
||||
val.SetAttribute("TIP", i18n.Lng(i18n.TipEffortWebp))
|
||||
name = i18n.Str(i18n.EffortMethod)
|
||||
case "avif":
|
||||
val.SetAttributes("MIN=0, MAX=10, SHOWTICKS=11, VALUE=10")
|
||||
val.SetAttribute("TIP", i18n.Lng(i18n.TipEffortAvif))
|
||||
name = i18n.Str(i18n.EffortSpeed)
|
||||
case "jxl":
|
||||
val.SetAttributes("MIN=1, MAX=10, SHOWTICKS=10, VALUE=7")
|
||||
val.SetAttribute("TIP", i18n.Lng(i18n.TipEffortJxl))
|
||||
name = i18n.Str(i18n.EffortEffort)
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
val.SetAttribute("EFFORTNAME", name)
|
||||
iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", name, val.GetInt("VALUE")))
|
||||
iup.Refresh(iup.GetHandle("LabelEffort"))
|
||||
}
|
||||
|
||||
// tableRowColors sets the alternating row colors for dark or light mode.
|
||||
func tableRowColors(t iup.Ihandle, dark bool) {
|
||||
even, odd := "#F0F0F0", "#FFFFFF"
|
||||
if dark {
|
||||
even, odd = "#3A3A3A", "#2D2D2D"
|
||||
}
|
||||
t.SetAttribute("EVENROWCOLOR", even)
|
||||
t.SetAttribute("ODDROWCOLOR", odd)
|
||||
}
|
||||
|
||||
func list() iup.Ihandle {
|
||||
t := iup.Table().SetHandle("Table")
|
||||
t.SetAttributes(map[string]string{
|
||||
"EXPAND": "YES",
|
||||
"NUMCOL": "3",
|
||||
"NUMLIN": "0",
|
||||
"TITLE1": i18n.Lng(i18n.ColTitle),
|
||||
"TITLE2": i18n.Lng(i18n.ColType),
|
||||
"TITLE3": i18n.Lng(i18n.ColSize),
|
||||
"WIDTH1": "250",
|
||||
"WIDTH2": "50",
|
||||
"WIDTH3": "100",
|
||||
"ALIGNMENT2": "ACENTER",
|
||||
"ALIGNMENT3": "ARIGHT",
|
||||
"SELECTIONMODE": "SINGLE",
|
||||
"USERRESIZE": "YES",
|
||||
"STRETCHLAST": "NO",
|
||||
"FOCUSRECT": "NO",
|
||||
"SORTABLE": "YES",
|
||||
"ALTERNATECOLOR": "YES",
|
||||
})
|
||||
|
||||
tableRowColors(t, iup.GetGlobal("DARKMODE") == "YES" && iup.GetGlobal("AUTODARKMODE") == "YES")
|
||||
|
||||
t.SetCallback("ENTERITEM_CB", iup.EnterItemFunc(func(ih iup.Ihandle, lin, col int) int {
|
||||
index = lin - 1
|
||||
setActive()
|
||||
previewPost()
|
||||
|
||||
return iup.DEFAULT
|
||||
}))
|
||||
|
||||
t.SetCallback("SORT_CB", iup.TableSortFunc(onSort))
|
||||
|
||||
t.SetCallback("DROPFILES_CB", iup.DropFilesFunc(func(ih iup.Ihandle, fileName string, num, x, y int) int {
|
||||
dec, err := url.QueryUnescape(fileName)
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
conv := cbconvert.New(options())
|
||||
|
||||
fs, err := conv.Files([]string{dec})
|
||||
if err != nil {
|
||||
iup.PostMessage(iup.GetHandle("dlg"), err.Error(), 0, 0)
|
||||
fmt.Println(err)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
addFiles(fs)
|
||||
|
||||
return iup.DEFAULT
|
||||
}))
|
||||
|
||||
return iup.Vbox(t)
|
||||
}
|
||||
|
||||
func preview() iup.Ihandle {
|
||||
return iup.Frame(
|
||||
iup.Vbox(
|
||||
iup.Canvas().SetAttributes("EXPAND=YES, MINSIZE=400x, BORDER=NO").SetHandle("Preview").
|
||||
SetCallback("ACTION", iup.ActionFunc(drawPreview)).
|
||||
SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(previewMessage)),
|
||||
iup.Label("").SetAttributes("EXPAND=HORIZONTAL, ALIGNMENT=ACENTER").SetHandle("PreviewInfo"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// previewTheme holds the preview canvas colors for light or dark mode.
|
||||
type previewTheme struct {
|
||||
gradTop, gradBottom string
|
||||
card, cardBorder string
|
||||
frame, shadow string
|
||||
}
|
||||
|
||||
func previewThemeFor(dark bool) previewTheme {
|
||||
if dark {
|
||||
return previewTheme{
|
||||
gradTop: "#3A3F45", gradBottom: "#202327",
|
||||
card: "#FFFFFF", cardBorder: "#16181B",
|
||||
frame: "#101214", shadow: "#141619",
|
||||
}
|
||||
}
|
||||
|
||||
return previewTheme{
|
||||
gradTop: "#F7F9FC", gradBottom: "#DBE2EA",
|
||||
card: "#FFFFFF", cardBorder: "#CDD5DE",
|
||||
frame: "#BFC8D2", shadow: "#B7C0CB",
|
||||
}
|
||||
}
|
||||
|
||||
// drawPreview paints a gradient background, then the cover framed with a drop shadow, or an empty page card with the logo.
|
||||
func drawPreview(ih iup.Ihandle) int {
|
||||
iup.DrawBegin(ih)
|
||||
defer iup.DrawEnd(ih)
|
||||
|
||||
cw, ch := iup.DrawGetSize(ih)
|
||||
th := previewThemeFor(iup.GetGlobal("DARKMODE") == "YES" && iup.GetGlobal("AUTODARKMODE") == "YES")
|
||||
|
||||
iup.DrawLinearGradient(ih, 0, 0, cw, ch, 90, th.gradTop, th.gradBottom)
|
||||
|
||||
margin := cw / 24
|
||||
if m := ch / 24; m < margin {
|
||||
margin = m
|
||||
}
|
||||
if margin < 12 {
|
||||
margin = 12
|
||||
}
|
||||
|
||||
if hasCover {
|
||||
drawCover(ih, th, cw, ch, margin)
|
||||
} else {
|
||||
drawPlaceholder(ih, th, cw, ch, margin)
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
// drawCover draws the loaded cover scaled to fit, with a drop shadow and a thin frame.
|
||||
func drawCover(ih iup.Ihandle, th previewTheme, cw, ch, margin int) {
|
||||
iw, ihh, _ := iup.DrawGetImageInfo("cover")
|
||||
if iw <= 0 || ihh <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s := math.Min(float64(cw-2*margin)/float64(iw), float64(ch-2*margin)/float64(ihh))
|
||||
dw, dh := int(float64(iw)*s), int(float64(ihh)*s)
|
||||
x, y := (cw-dw)/2, (ch-dh)/2
|
||||
|
||||
const off = 6
|
||||
ih.SetAttribute("DRAWSTYLE", "FILL")
|
||||
ih.SetAttribute("DRAWCOLOR", th.shadow)
|
||||
iup.DrawRectangle(ih, x+off, y+off, x+dw+off, y+dh+off)
|
||||
|
||||
iup.DrawImage(ih, "cover", x, y, dw, dh)
|
||||
|
||||
ih.SetAttribute("DRAWSTYLE", "STROKE")
|
||||
ih.SetAttribute("DRAWLINEWIDTH", "1")
|
||||
ih.SetAttribute("DRAWCOLOR", th.frame)
|
||||
iup.DrawRectangle(ih, x, y, x+dw, y+dh)
|
||||
}
|
||||
|
||||
// drawPlaceholder draws an empty comic-page card with the logo centered, shown until a cover is loaded.
|
||||
func drawPlaceholder(ih iup.Ihandle, th previewTheme, cw, ch, margin int) {
|
||||
cardH := ch - 2*margin
|
||||
cardW := cardH * 2 / 3
|
||||
if cardW > cw-2*margin {
|
||||
cardW = cw - 2*margin
|
||||
cardH = cardW * 3 / 2
|
||||
}
|
||||
x, y := (cw-cardW)/2, (ch-cardH)/2
|
||||
|
||||
r := cardW / 14
|
||||
if r < 8 {
|
||||
r = 8
|
||||
}
|
||||
|
||||
const off = 7
|
||||
ih.SetAttribute("DRAWSTYLE", "FILL")
|
||||
ih.SetAttribute("DRAWCOLOR", th.shadow)
|
||||
iup.DrawRoundedRectangle(ih, x+off, y+off, x+cardW+off, y+cardH+off, r)
|
||||
|
||||
ih.SetAttribute("DRAWCOLOR", th.card)
|
||||
iup.DrawRoundedRectangle(ih, x, y, x+cardW, y+cardH, r)
|
||||
|
||||
ih.SetAttribute("DRAWSTYLE", "STROKE")
|
||||
ih.SetAttribute("DRAWLINEWIDTH", "1")
|
||||
ih.SetAttribute("DRAWCOLOR", th.cardBorder)
|
||||
iup.DrawRoundedRectangle(ih, x, y, x+cardW, y+cardH, r)
|
||||
|
||||
lw, lh, _ := iup.DrawGetImageInfo("logo")
|
||||
if lw <= 0 || lh <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
s := float64(cardW*2/5) / float64(lw)
|
||||
dw, dh := int(float64(lw)*s), int(float64(lh)*s)
|
||||
iup.DrawImage(ih, "logo", x+(cardW-dw)/2, y+(cardH-dh)/2, dw, dh)
|
||||
}
|
||||
|
||||
// previewMessage receives a rendered cover from previewRender and triggers a canvas redraw.
|
||||
func previewMessage(ih iup.Ihandle, s string, i int, p any) int {
|
||||
if i != previewPage {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
img := p.(cbconvert.Image)
|
||||
iup.GetHandle("Loading").SetAttributes("VISIBLE=NO, STOP=YES")
|
||||
|
||||
if img.Image != nil && len(s) == 0 {
|
||||
iup.Destroy(iup.GetHandle("cover"))
|
||||
iup.ImageFromImage(img.Image).SetHandle("cover")
|
||||
hasCover = true
|
||||
iup.GetHandle("PreviewInfo").SetAttribute("TITLE", fmt.Sprintf("%s (%dx%d)", img.SizeHuman, img.Width, img.Height))
|
||||
} else {
|
||||
iup.Destroy(iup.GetHandle("cover"))
|
||||
hasCover = false
|
||||
iup.GetHandle("PreviewInfo").SetAttribute("TITLE", "")
|
||||
|
||||
sp := strings.Split(s, ": ")
|
||||
if len(sp) > 1 {
|
||||
iup.MessageError(ih, fmt.Sprintf("%s\n\n%s", sp[0], strings.Join(sp[1:], ": ")))
|
||||
}
|
||||
}
|
||||
|
||||
iup.Update(ih)
|
||||
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
// pageBox is the page-navigation spin shown in the status bar; hidden until a comic is selected.
|
||||
func pageBox() iup.Ihandle {
|
||||
return iup.Hbox(
|
||||
iup.Space().SetAttribute("SIZE", "5"),
|
||||
iup.Label(i18n.Lng(i18n.LblPage)),
|
||||
iup.Space().SetAttribute("SIZE", "3"),
|
||||
iup.Text().SetAttributes(`SPIN=YES, SPINMIN=1, SPINMAX=1, VALUE=1, VISIBLECOLUMNS=3, MASK="/d*"`).SetHandle("Page").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipPage)).
|
||||
SetCallback("SPIN_CB", iup.SpinFunc(func(ih iup.Ihandle, pos int) int {
|
||||
return onPageChanged()
|
||||
})).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
return onPageChanged()
|
||||
})).
|
||||
SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(func(ih iup.Ihandle, s string, i int, p any) int {
|
||||
if s != previewPath {
|
||||
return iup.DEFAULT
|
||||
}
|
||||
|
||||
ih.SetAttribute("SPINMAX", strconv.Itoa(i))
|
||||
iup.GetHandle("PageCount").SetAttribute("TITLE", fmt.Sprintf("/ %d", i))
|
||||
|
||||
if previewPage > i-1 {
|
||||
previewPage = i - 1
|
||||
}
|
||||
if previewPage < 0 {
|
||||
previewPage = 0
|
||||
}
|
||||
ih.SetAttribute("VALUE", strconv.Itoa(previewPage+1))
|
||||
|
||||
iup.Refresh(iup.GetHandle("PageBox"))
|
||||
|
||||
previewRender()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
iup.Space().SetAttribute("SIZE", "3"),
|
||||
iup.Label("").SetHandle("PageCount"),
|
||||
).SetAttributes("ALIGNMENT=ACENTER, VISIBLE=NO").SetHandle("PageBox")
|
||||
}
|
||||
|
||||
func tabInput() iup.Ihandle {
|
||||
return iup.Hbox(
|
||||
iup.Vbox(
|
||||
iup.Toggle(i18n.Lng(i18n.TglRecursive)).SetHandle("Recursive").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipRecursive)),
|
||||
iup.Toggle(i18n.Lng(i18n.TglNoRGB)).SetHandle("NoRGB").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipNoRGB)),
|
||||
iup.Toggle(i18n.Lng(i18n.TglNoCover)).SetHandle("NoCover").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipNoCover)),
|
||||
iup.Toggle(i18n.Lng(i18n.TglNoNonImage)).SetHandle("NoNonImage").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipNoNonImage)),
|
||||
iup.Toggle(i18n.Lng(i18n.TglNoConvert)).SetHandle("NoConvert").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipNoConvert)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
).SetAttributes("NGAP=10"),
|
||||
iup.Space().SetAttribute("SIZE", "15"),
|
||||
iup.Vbox(
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblMinSize)),
|
||||
iup.Text().SetAttributes(`SPIN=YES, SPINMAX=2048, VISIBLECOLUMNS=4, MASK="/d*"`).SetHandle("Size").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipSize)),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblDPI)),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"EDITBOX": "YES",
|
||||
"VISIBLECOLUMNS": "6",
|
||||
"VALUE": "Default",
|
||||
"1": "Default",
|
||||
"2": "150",
|
||||
"3": "300",
|
||||
"4": "600",
|
||||
"5": "1200",
|
||||
}).SetHandle("DPI").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipDPI)),
|
||||
),
|
||||
).SetAttributes("NGAP=10"),
|
||||
).SetHandle("VboxInput")
|
||||
}
|
||||
|
||||
func tabOutput() iup.Ihandle {
|
||||
return iup.Hbox(
|
||||
iup.Vbox(
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblOutDir)),
|
||||
iup.Text().SetAttributes("VISIBLECOLUMNS=16, MINSIZE=100x").SetHandle("OutDir").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipOutDir)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
iup.Space().SetAttribute("SIZE", "5x0"),
|
||||
iup.Button(i18n.Lng(i18n.BtnBrowse)).SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onOutputDirectory)),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblSuffix)),
|
||||
iup.Text().SetAttributes("VISIBLECOLUMNS=16, MINSIZE=100x").SetHandle("Suffix").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipSuffix)),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblArchive)),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"VALUE": "1",
|
||||
"1": "ZIP",
|
||||
"2": "TAR",
|
||||
}).SetHandle("Archive").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipArchive)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblCompression)),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"VALUE": "1",
|
||||
"1": "Default",
|
||||
"2": "Store (none)",
|
||||
"3": "1",
|
||||
"4": "2",
|
||||
"5": "3",
|
||||
"6": "4",
|
||||
"7": "5",
|
||||
"8": "6",
|
||||
"9": "7",
|
||||
"10": "8",
|
||||
"11": "9",
|
||||
}).SetHandle("ZipLevel").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipZipLevel)),
|
||||
).SetHandle("VboxZipLevel"),
|
||||
).SetAttributes("NGAP=10"),
|
||||
iup.Space().SetAttribute("SIZE", "15"),
|
||||
iup.Vbox(
|
||||
iup.Vbox(
|
||||
iup.Toggle(i18n.Lng(i18n.TglCombine)).SetHandle("Combine").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipCombine)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblOutFile)),
|
||||
iup.Text().SetAttributes("VISIBLECOLUMNS=16, MINSIZE=100x").SetHandle("OutFile").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipOutFile)),
|
||||
iup.Space().SetAttribute("SIZE", "5x0"),
|
||||
iup.Button(i18n.Lng(i18n.BtnBrowse)).SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onOutputFile)),
|
||||
).SetHandle("VboxOutFile"),
|
||||
).SetAttributes("NGAP=10"),
|
||||
).SetHandle("VboxOutput")
|
||||
}
|
||||
|
||||
func tabImage() iup.Ihandle {
|
||||
return iup.Hbox(
|
||||
iup.Vbox(
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblFormat)),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"VALUE": "1",
|
||||
"1": "JPEG",
|
||||
"2": "PNG",
|
||||
"3": "TIFF",
|
||||
"4": "BMP",
|
||||
"5": "WEBP",
|
||||
"6": "AVIF",
|
||||
"7": "JXL",
|
||||
}).SetHandle("Format").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipFormat)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setEffort(strings.ToLower(ih.GetAttribute("VALUESTRING")))
|
||||
setActive()
|
||||
previewPost()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblSize)),
|
||||
iup.Hbox(
|
||||
iup.Text().SetAttributes(`VISIBLECOLUMNS=6, MASK="/d*"`).SetHandle("Width").
|
||||
SetAttribute("CUEBANNER", i18n.Lng(i18n.CueWidth)).
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipWidthHeight)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||
|
||||
return iup.DEFAULT
|
||||
})).
|
||||
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||
if ih.GetAttribute("MYVALUE") != "" {
|
||||
previewPost()
|
||||
}
|
||||
ih.SetAttribute("MYVALUE", "")
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
iup.Space().SetAttribute("SIZE", "2"),
|
||||
iup.Label("x"),
|
||||
iup.Space().SetAttribute("SIZE", "2"),
|
||||
iup.Text().SetAttributes(`VISIBLECOLUMNS=6, MASK="/d*"`).SetHandle("Height").
|
||||
SetAttribute("CUEBANNER", i18n.Lng(i18n.CueHeight)).
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipWidthHeight)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
setActive()
|
||||
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||
|
||||
return iup.DEFAULT
|
||||
})).
|
||||
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||
if ih.GetAttribute("MYVALUE") != "" {
|
||||
previewPost()
|
||||
}
|
||||
ih.SetAttribute("MYVALUE", "")
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=0"),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Toggle(i18n.Lng(i18n.TglFit)).SetHandle("Fit").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipFit)),
|
||||
iup.Toggle(i18n.Lng(i18n.TglNoUpscale)).SetHandle("NoUpscale").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipNoUpscale)),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblFilter)),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"VALUE": "3",
|
||||
"TIP": i18n.Lng(i18n.FilterLinear),
|
||||
"1": "NearestNeighbor",
|
||||
"2": "Box",
|
||||
"3": "Linear",
|
||||
"4": "MitchellNetravali",
|
||||
"5": "CatmullRom",
|
||||
"6": "Gaussian",
|
||||
"7": "Lanczos",
|
||||
}).SetHandle("Filter").SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(onFilterChanged)),
|
||||
),
|
||||
).SetAttributes("NGAP=10"),
|
||||
iup.Space().SetAttribute("SIZE", "15"),
|
||||
iup.Vbox(
|
||||
iup.Vbox(
|
||||
iup.Hbox(
|
||||
iup.Label(i18n.Lng(i18n.LblQuality)),
|
||||
iup.Label("75").SetHandle("LabelQuality"),
|
||||
).SetAttributes("NMARGIN=0"),
|
||||
iup.Val("").SetAttributes(`MIN=0, MAX=100, VALUE=75, SHOWTICKS=10`).SetHandle("Quality").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipQuality)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
iup.GetHandle("LabelQuality").SetAttribute("TITLE", ih.GetInt("VALUE"))
|
||||
iup.Refresh(iup.GetHandle("LabelQuality"))
|
||||
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||
|
||||
return iup.DEFAULT
|
||||
})).
|
||||
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||
if ih.GetAttribute("MYVALUE") != "" {
|
||||
previewPost()
|
||||
}
|
||||
ih.SetAttribute("MYVALUE", "")
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
).SetHandle("VboxQuality"),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblEffort)).SetHandle("LabelEffort"),
|
||||
iup.Val("").SetAttributes(`MIN=0, MAX=10, VALUE=0, SHOWTICKS=11`).SetHandle("Effort").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipEffort)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
iup.GetHandle("LabelEffort").SetAttribute("TITLE", fmt.Sprintf("%s: %d", ih.GetAttribute("EFFORTNAME"), ih.GetInt("VALUE")))
|
||||
iup.Refresh(iup.GetHandle("LabelEffort"))
|
||||
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||
|
||||
return iup.DEFAULT
|
||||
})).
|
||||
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||
if ih.GetAttribute("MYVALUE") != "" {
|
||||
previewPost()
|
||||
}
|
||||
ih.SetAttribute("MYVALUE", "")
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
).SetHandle("VboxEffort"),
|
||||
iup.Vbox(
|
||||
iup.Toggle(i18n.Lng(i18n.TglLossless)).SetHandle("Lossless").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipLossless)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
userLossless = ih.GetAttribute("VALUE") == "ON"
|
||||
setActive()
|
||||
previewPost()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Toggle(i18n.Lng(i18n.TglGrayscale)).SetHandle("Grayscale").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipGrayscale)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
previewPost()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
).SetAttributes("NGAP=10"),
|
||||
).SetHandle("VboxImage")
|
||||
}
|
||||
|
||||
func tabTransform() iup.Ihandle {
|
||||
return iup.Vbox(
|
||||
iup.Vbox(
|
||||
iup.Hbox(
|
||||
iup.Label(i18n.Lng(i18n.LblBrightness)),
|
||||
iup.Label("0").SetHandle("LabelBrightness"),
|
||||
).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=0"),
|
||||
iup.Val("").SetAttributes(`MIN=-100, MAX=100, VALUE=0, SHOWTICKS=10`).SetHandle("Brightness").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipBrightness)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
iup.GetHandle("LabelBrightness").SetAttribute("TITLE", iup.GetHandle("Brightness").GetInt("VALUE"))
|
||||
iup.Refresh(iup.GetHandle("LabelBrightness"))
|
||||
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||
|
||||
return iup.DEFAULT
|
||||
})).
|
||||
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||
if ih.GetAttribute("MYVALUE") != "" {
|
||||
previewPost()
|
||||
}
|
||||
ih.SetAttribute("MYVALUE", "")
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Hbox(
|
||||
iup.Label(i18n.Lng(i18n.LblContrast)),
|
||||
iup.Label("0").SetHandle("LabelContrast"),
|
||||
).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=0"),
|
||||
iup.Val("").SetAttributes(`MIN=-100, MAX=100, VALUE=0, SHOWTICKS=10`).SetHandle("Contrast").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipContrast)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
iup.GetHandle("LabelContrast").SetAttribute("TITLE", iup.GetHandle("Contrast").GetInt("VALUE"))
|
||||
iup.Refresh(iup.GetHandle("LabelContrast"))
|
||||
ih.SetAttribute("MYVALUE", ih.GetInt("VALUE"))
|
||||
|
||||
return iup.DEFAULT
|
||||
})).
|
||||
SetCallback("KILLFOCUS_CB", iup.KillFocusFunc(func(ih iup.Ihandle) int {
|
||||
if ih.GetAttribute("MYVALUE") != "" {
|
||||
previewPost()
|
||||
}
|
||||
ih.SetAttribute("MYVALUE", "")
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblRotate)),
|
||||
iup.List().SetAttributes(map[string]string{
|
||||
"DROPDOWN": "YES",
|
||||
"VALUE": "1",
|
||||
"1": "0",
|
||||
"2": "90",
|
||||
"3": "180",
|
||||
"4": "270",
|
||||
}).SetHandle("Rotate").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipRotate)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(func(ih iup.Ihandle) int {
|
||||
previewPost()
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
),
|
||||
).SetHandle("VboxTransform").SetAttributes("NGAP=10")
|
||||
}
|
||||
|
||||
func tabs() iup.Ihandle {
|
||||
return iup.Tabs(
|
||||
tabInput().SetAttributes("TABTITLE="+i18n.Lng(i18n.TabInput)+", NMARGIN=10x10"),
|
||||
tabOutput().SetAttributes("TABTITLE="+i18n.Lng(i18n.TabOutput)+", NMARGIN=10x10"),
|
||||
tabImage().SetAttributes("TABTITLE="+i18n.Lng(i18n.TabImage)+", NMARGIN=10x10"),
|
||||
tabTransform().SetAttributes("TABTITLE="+i18n.Lng(i18n.TabTransform)+", NMARGIN=10x10"),
|
||||
).SetHandle("Tabs")
|
||||
}
|
||||
|
||||
func buttons() iup.Ihandle {
|
||||
addFiles := iup.Button(i18n.Lng(i18n.BtnAddFiles)).SetHandle("AddFiles").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onAddFiles))
|
||||
addDir := iup.Button(i18n.Lng(i18n.BtnAddDir)).SetHandle("AddDir").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onAddDir))
|
||||
remove := iup.Button(i18n.Lng(i18n.BtnRemove)).SetHandle("Remove").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onRemove))
|
||||
removeAll := iup.Button(i18n.Lng(i18n.BtnRemoveAll)).SetHandle("RemoveAll").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onRemoveAll))
|
||||
thumbnail := iup.Button(i18n.Lng(i18n.BtnThumbnail)).SetHandle("Thumbnail").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onThumbnail))
|
||||
cover := iup.Button(i18n.Lng(i18n.BtnCover)).SetHandle("Cover").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onCover))
|
||||
convert := iup.Button(i18n.Lng(i18n.BtnConvert)).SetHandle("Convert").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetCallback("ACTION", iup.ActionFunc(onConvert))
|
||||
reset := iup.Button(i18n.Lng(i18n.BtnReset)).SetHandle("Reset").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipReset)).
|
||||
SetCallback("ACTION", iup.ActionFunc(onReset))
|
||||
save := iup.Button(i18n.Lng(i18n.BtnSave)).SetHandle("Save").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipSave)).
|
||||
SetCallback("ACTION", iup.ActionFunc(onSave))
|
||||
|
||||
command := iup.Button(i18n.Lng(i18n.BtnCommand)).SetHandle("Command").SetAttributes("PADDING=DEFAULTBUTTONPADDING").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipCommand)).
|
||||
SetCallback("ACTION", iup.ActionFunc(onCommand))
|
||||
|
||||
profile := iup.List().SetAttributes("DROPDOWN=YES").SetHandle("Profile").
|
||||
SetAttribute("TIP", i18n.Lng(i18n.TipProfile)).
|
||||
SetCallback("VALUECHANGED_CB", iup.ValueChangedFunc(onProfileSelect))
|
||||
|
||||
iup.Normalizer(addFiles, addDir, remove, removeAll, thumbnail, cover, convert, reset, save, command).SetAttribute("NORMALIZE", "BOTH")
|
||||
iup.Normalizer(addFiles, addDir, remove, removeAll, thumbnail, cover, convert, reset, save, command, profile).SetAttribute("NORMALIZE", "HORIZONTAL")
|
||||
|
||||
return iup.Vbox(
|
||||
iup.Vbox(
|
||||
addFiles,
|
||||
addDir,
|
||||
remove,
|
||||
removeAll,
|
||||
).SetAttribute("NGAP", "2"),
|
||||
iup.Space().SetAttribute("SIZE", "x5"),
|
||||
iup.Vbox(
|
||||
thumbnail,
|
||||
cover,
|
||||
).SetAttribute("NGAP", "2"),
|
||||
iup.Space().SetAttribute("SIZE", "x5"),
|
||||
iup.Vbox(
|
||||
convert,
|
||||
),
|
||||
iup.Fill(),
|
||||
iup.Vbox(
|
||||
iup.Label(i18n.Lng(i18n.LblProfile)),
|
||||
profile,
|
||||
reset,
|
||||
save,
|
||||
command,
|
||||
).SetAttribute("NGAP", "2"),
|
||||
).SetHandle("Buttons").SetAttributes("ALIGNMENT=ACENTER")
|
||||
}
|
||||
|
||||
func status() iup.Ihandle {
|
||||
return iup.Hbox(
|
||||
loading(),
|
||||
pageBox(),
|
||||
iup.Fill(),
|
||||
iup.Label("File 1 of 1").SetHandle("LabelStatus1").SetAttributes("VISIBLE=NO"),
|
||||
iup.Space().SetAttribute("SIZE", "5"),
|
||||
iup.Label("(000/000)").SetHandle("LabelStatus2").SetAttributes("VISIBLE=NO"),
|
||||
iup.Space().SetAttribute("SIZE", "5"),
|
||||
iup.ProgressBar().SetAttributes("VISIBLE=NO").SetHandle("ProgressBar").
|
||||
SetCallback("POSTMESSAGE_CB", iup.PostMessageFunc(func(ih iup.Ihandle, s string, i int, p any) int {
|
||||
switch s {
|
||||
case "convert":
|
||||
conv := p.(*cbconvert.Converter)
|
||||
ih.SetAttributes("VALUE=0, VISIBLE=YES")
|
||||
ih.SetAttribute("MAX", conv.Ncontents)
|
||||
|
||||
iup.GetHandle("LabelStatus1").SetAttribute("TITLE", fmt.Sprintf(i18n.Str(i18n.StatusFileOf), conv.CurrFile, conv.Nfiles))
|
||||
iup.GetHandle("LabelStatus1").SetAttributes("VISIBLE=YES")
|
||||
iup.GetHandle("LabelStatus2").SetAttributes("VISIBLE=YES")
|
||||
|
||||
iup.Refresh(iup.GetHandle("StatusBar"))
|
||||
case "start":
|
||||
conv := p.(*cbconvert.Converter)
|
||||
ih.SetAttributes("VALUE=0, VISIBLE=YES")
|
||||
ih.SetAttribute("MAX", conv.Nfiles)
|
||||
|
||||
iup.GetHandle("LabelStatus2").SetAttributes("VISIBLE=YES")
|
||||
case "progress":
|
||||
conv := p.(*cbconvert.Converter)
|
||||
ih.SetAttribute("VALUE", conv.CurrContent)
|
||||
iup.GetHandle("LabelStatus2").SetAttribute("TITLE", fmt.Sprintf("(%03d/%03d)", conv.CurrContent, conv.Ncontents))
|
||||
|
||||
iup.Refresh(iup.GetHandle("StatusBar"))
|
||||
case "progress2":
|
||||
conv := p.(*cbconvert.Converter)
|
||||
ih.SetAttribute("VALUE", conv.CurrFile)
|
||||
iup.GetHandle("LabelStatus2").SetAttribute("TITLE", fmt.Sprintf("(%03d/%03d)", conv.CurrFile, conv.Nfiles))
|
||||
|
||||
iup.Refresh(iup.GetHandle("StatusBar"))
|
||||
case "finish":
|
||||
setBusy(false)
|
||||
|
||||
iup.GetHandle("LabelStatus1").SetAttributes(`TITLE="", VISIBLE=NO`)
|
||||
iup.GetHandle("LabelStatus2").SetAttributes(`TITLE="", VISIBLE=NO`)
|
||||
ih.SetAttributes("VALUE=0, VISIBLE=NO")
|
||||
|
||||
iup.Refresh(iup.GetHandle("StatusBar"))
|
||||
|
||||
iup.GetHandle("dlg").SetCallback("K_ANY", nil)
|
||||
iup.GetHandle("dlg").SetCallback("CLOSE_CB", nil)
|
||||
}
|
||||
|
||||
return iup.DEFAULT
|
||||
})),
|
||||
iup.Space().SetAttribute("SIZE", "5x0"),
|
||||
).SetAttributes("ALIGNMENT=ACENTER, NMARGIN=5x5").SetHandle("StatusBar")
|
||||
}
|
||||
|
||||
func loading() iup.Ihandle {
|
||||
img, _ := gif.DecodeAll(bytes.NewReader(appLoading))
|
||||
animation := iup.User()
|
||||
|
||||
for idx, i := range img.Image {
|
||||
name := fmt.Sprintf("Loading%d", idx)
|
||||
iup.ImageFromImage(i).SetHandle(name)
|
||||
iup.Append(animation, iup.GetHandle(name))
|
||||
}
|
||||
|
||||
return iup.AnimatedLabel(animation).SetAttributes("VISIBLE=NO").SetHandle("Loading")
|
||||
}
|
||||
@@ -1,26 +1,58 @@
|
||||
module github.com/gen2brain/cbconvert/cmd/cbconvert
|
||||
|
||||
go 1.21
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/gen2brain/cbconvert v1.0.0
|
||||
github.com/schollz/progressbar/v3 v3.13.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/gen2brain/cbconvert v1.2.0
|
||||
github.com/schollz/progressbar/v3 v3.19.0
|
||||
golang.org/x/term v0.44.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chai2010/webp v1.1.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
github.com/anthonynsimon/bild v0.15.0 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.4 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d // indirect
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.10.1 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/gen2brain/go-fitz v1.23.1 // indirect
|
||||
github.com/gen2brain/go-unarr v0.1.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/gen2brain/avif v0.5.1 // indirect
|
||||
github.com/gen2brain/go-fitz v1.28.1 // indirect
|
||||
github.com/gen2brain/jpegli v0.4.1 // indirect
|
||||
github.com/gen2brain/jpegn v0.4.2 // indirect
|
||||
github.com/gen2brain/jpegxl v0.5.2 // indirect
|
||||
github.com/gen2brain/webp v0.6.1 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.24 // indirect
|
||||
github.com/mholt/archives v0.1.5 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.1.1 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/rivo/uniseg v0.3.4 // indirect
|
||||
golang.org/x/image v0.12.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/term v0.6.0 // indirect
|
||||
gopkg.in/gographics/imagick.v3 v3.4.3 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/stangelandcl/ppmd v0.1.1 // indirect
|
||||
github.com/tetratelabs/wazero v1.12.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/image v0.43.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,79 +1,157 @@
|
||||
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
|
||||
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
|
||||
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/anthonynsimon/bild v0.15.0 h1:FzvaNLuNlAPKw1Xz7V2WYOcGIEBMj8Y6ZyAk7CI+HzA=
|
||||
github.com/anthonynsimon/bild v0.15.0/go.mod h1:qIgJ9FldkCn0iy5Ad24fzUkz5R+iJ0WfhiV+6FeCB5A=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.4 h1:iHiVJfxbrB6RF4X+snI2MpVgNBKmVfGaTqZGNlMQIU0=
|
||||
github.com/bodgit/sevenzip v1.6.4/go.mod h1:ZtNi5KNgHXeXg1G7WiF0IWSuFE2eG6lt/cTGlvuirO0=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
|
||||
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
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/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d h1:yeH8wrJa3+8uKKDAdURHUK1ds2UvKhMqX2MiOdVeKPs=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210512043655-120bcdb2a55e/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
|
||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d h1:8+qI8ant/vZkNSsbwSjIR6XJfWcDVTg/qx/3pRUUZNA=
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d/go.mod h1:yTR3tKgyk20phAFg6IE9ulMA5NjEDD2wyx+okRFLVtw=
|
||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4=
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
|
||||
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
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 v1.0.0 h1:6szCltgrC/Vq+XuknuGKjS6E1J5zRLmqCnHXnvALCvY=
|
||||
github.com/gen2brain/cbconvert v1.0.0/go.mod h1:3zoSNHGpunxvS5cXpge9XwU585Zxsr4ly13dryeQCGk=
|
||||
github.com/gen2brain/go-fitz v1.23.1 h1:x69/szWZXpI3jZ57mMqCg7WqqvtYnQG0lXts3L6M1Fc=
|
||||
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/go.mod h1:MK9a3hddpaIxjEtrE1f/LA5yJ7gA34cS7Oyr325sY9s=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/gen2brain/avif v0.5.1 h1:LQzLsJpWyGlsa4wuZ3D57qEbCiICIK7Yidz5ZPEwzTk=
|
||||
github.com/gen2brain/avif v0.5.1/go.mod h1:QgrYqdVE9y40PCfArK9VakcMIpYeDYpZmCSLkW6C1n8=
|
||||
github.com/gen2brain/cbconvert v1.2.0 h1:eUs5MPzft+Lo9ADMYYQ5AzydO54Pv0YYUTm2lPo3XDo=
|
||||
github.com/gen2brain/cbconvert v1.2.0/go.mod h1:/6QtSgf47bDBmtkidNoYwNMHaebsDhPTKnDMmyNUd9E=
|
||||
github.com/gen2brain/go-fitz v1.28.1 h1:ToEYb2vN4ByaL2VmRNGk92Sa1UAkCn8bsObpA3WkQ48=
|
||||
github.com/gen2brain/go-fitz v1.28.1/go.mod h1:pY2hqAjp9Zy7qfPI2gwbJMHBFAdZpVXOLrRxD82l3Bs=
|
||||
github.com/gen2brain/jpegli v0.4.1 h1:qc11IQU0jTYFltroulT4MXmbu9YRftqHV0YBZ0Bqz5o=
|
||||
github.com/gen2brain/jpegli v0.4.1/go.mod h1:zJ++s4symmKCN1CLkrY0dGXTY3s0NWbd94Rz9KLdCzk=
|
||||
github.com/gen2brain/jpegn v0.4.2 h1:sxy2yolV1eNA02uYtnqBFm4EIC3ETnars98aG7Dc4LM=
|
||||
github.com/gen2brain/jpegn v0.4.2/go.mod h1:YvcVOmVPSAsefH6yn9HBW3uY0EHlZwCMoiJXoAWfgL0=
|
||||
github.com/gen2brain/jpegxl v0.5.2 h1:1ou9YRziU8PbpkfFJIyxrNjYM+WaMl2n9LloABxkKsU=
|
||||
github.com/gen2brain/jpegxl v0.5.2/go.mod h1:Wlc6lqx03RJfhiQRyHa2e+8VQwT4/qv7zSRsNv9T+yE=
|
||||
github.com/gen2brain/webp v0.6.1 h1:ei7Y1SWpQcdqz3YNDNyn4y2nQanxs9WLzwW5/2DKS64=
|
||||
github.com/gen2brain/webp v0.6.1/go.mod h1:iGWMaCSw7t3I/Cv9llzEKmpnR36S8lS8VL/ZVjxU0JE=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 h1:KeIaDS/+VEy/bhDYjG3Z78dOyLAU4HXcVxmd0WYHJTE=
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
|
||||
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
|
||||
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
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/nwaples/rardecode/v2 v2.2.5 h1:L5doqgGfQwI7qADJMqnkrSB86rpPsqQDrHeO0HWa5JY=
|
||||
github.com/nwaples/rardecode/v2 v2.2.5/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
||||
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
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.13.1 h1:o8rySDYiQ59Mwzy2FELeHY5ZARXZTVJC7iHD6PEFUiE=
|
||||
github.com/schollz/progressbar/v3 v3.13.1/go.mod h1:xvrbki8kfT1fzWzBT/UZd9L6GA+jdL7HAgq2RFnO6fQ=
|
||||
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/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/schollz/progressbar/v3 v3.19.0 h1:Ea18xuIRQXLAUidVDox3AbwfUhD0/1IvohyTutOIFoc=
|
||||
github.com/schollz/progressbar/v3 v3.19.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/stangelandcl/ppmd v0.1.1 h1:c25QazhlWUn5nmR1QOzafKhQxBicAr7GGCKER2aJ8H8=
|
||||
github.com/stangelandcl/ppmd v0.1.1/go.mod h1:Rrv7M+/2P5jYr/GMLhBl7Ug3uJ1bUiVzr5LbbaV6xgY=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
|
||||
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
|
||||
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.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
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.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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.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/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
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.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-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.3 h1:9plKFE/Us913jBN6KohtLG9FNW8LPvfpjiGAORIiEHg=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.3/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -2,17 +2,22 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/gen2brain/cbconvert"
|
||||
pb "github.com/schollz/progressbar/v3"
|
||||
flag "github.com/spf13/pflag"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
var appVersion string
|
||||
@@ -68,55 +73,79 @@ func main() {
|
||||
if _, err := os.Stat(opts.OutDir); err != nil {
|
||||
if err := os.MkdirAll(opts.OutDir, 0775); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conv.Initialize()
|
||||
defer conv.Terminate()
|
||||
|
||||
files, err := conv.Files(args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
interactive := !opts.Quiet && term.IsTerminal(int(os.Stderr.Fd()))
|
||||
|
||||
var bar *pb.ProgressBar
|
||||
if opts.Cover || opts.Thumbnail || opts.Meta {
|
||||
if !opts.Quiet {
|
||||
bar = pb.NewOptions(conv.Nfiles,
|
||||
pb.OptionShowCount(),
|
||||
pb.OptionClearOnFinish(),
|
||||
pb.OptionUseANSICodes(true),
|
||||
pb.OptionSetPredictTime(false),
|
||||
)
|
||||
newBar := func(max int, description string) {
|
||||
bar = pb.NewOptions(max,
|
||||
pb.OptionShowCount(),
|
||||
pb.OptionClearOnFinish(),
|
||||
pb.OptionUseANSICodes(true),
|
||||
pb.OptionSetPredictTime(false),
|
||||
pb.OptionSetDescription(description),
|
||||
)
|
||||
}
|
||||
|
||||
clearLine := func() {
|
||||
fmt.Fprint(os.Stderr, "\033[2K\r")
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
if interactive {
|
||||
if bar != nil {
|
||||
_ = bar.Finish()
|
||||
}
|
||||
clearLine()
|
||||
}
|
||||
}
|
||||
|
||||
if interactive && (opts.Cover || opts.Thumbnail || opts.Meta) {
|
||||
newBar(conv.Nfiles, "")
|
||||
}
|
||||
|
||||
conv.OnStart = func() {
|
||||
if !opts.Quiet {
|
||||
bar = pb.NewOptions(conv.Ncontents,
|
||||
pb.OptionShowCount(),
|
||||
pb.OptionClearOnFinish(),
|
||||
pb.OptionUseANSICodes(true),
|
||||
pb.OptionSetDescription(fmt.Sprintf("Converting %d of %d:", conv.CurrFile, conv.Nfiles)),
|
||||
pb.OptionSetPredictTime(false),
|
||||
)
|
||||
if interactive {
|
||||
clearLine()
|
||||
newBar(conv.Ncontents, fmt.Sprintf("Converting %d of %d:", conv.CurrFile, conv.Nfiles))
|
||||
}
|
||||
}
|
||||
|
||||
conv.OnProgress = func() {
|
||||
if !opts.Quiet {
|
||||
if bar != nil {
|
||||
_ = bar.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
conv.OnCompress = func() {
|
||||
if !opts.Quiet {
|
||||
fmt.Fprintf(os.Stderr, "Compressing %d of %d...\r", conv.CurrFile, conv.Nfiles)
|
||||
if interactive {
|
||||
if bar != nil {
|
||||
_ = bar.Finish()
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Compressing %d of %d...", conv.CurrFile, conv.Nfiles)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Combine {
|
||||
if err := conv.Combine(files); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cleanup()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
switch {
|
||||
case opts.Meta:
|
||||
@@ -134,14 +163,14 @@ func main() {
|
||||
|
||||
continue
|
||||
case opts.Cover:
|
||||
if err := conv.Cover(file.Path, file.Stat); err != nil {
|
||||
if err := conv.Cover(file); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
continue
|
||||
case opts.Thumbnail:
|
||||
if err = conv.Thumbnail(file.Path, file.Stat); err != nil {
|
||||
if err = conv.Thumbnail(file); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -149,13 +178,13 @@ func main() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := conv.Convert(file.Path, file.Stat); err != nil {
|
||||
if err := conv.Convert(file); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "\r")
|
||||
cleanup()
|
||||
}
|
||||
|
||||
// parseFlags parses command line flags.
|
||||
@@ -163,61 +192,86 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
opts := cbconvert.Options{}
|
||||
var args []string
|
||||
|
||||
base := defaultOptions()
|
||||
if len(os.Args) >= 2 {
|
||||
switch os.Args[1] {
|
||||
case "convert", "cover", "thumbnail":
|
||||
if name := profileArg(os.Args[2:]); name != "" {
|
||||
o, err := loadProfile(name)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
base = o
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var profile string
|
||||
const profileUsage = "Load a saved GUI profile as defaults; explicit flags still override"
|
||||
|
||||
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, avif, jxl")
|
||||
convert.StringVar(&opts.Archive, "archive", "zip", "Archive format, valid values are zip, tar")
|
||||
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.IntVar(&opts.Brightness, "brightness", 0, "Adjust the brightness of the images, must be in the range (-100, 100)")
|
||||
convert.IntVar(&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.IntVar(&opts.LevelsInMin, "levels-inmin", 0, "Shadow input value")
|
||||
convert.Float64Var(&opts.LevelsGamma, "levels-gamma", 1.0, "Midpoint/Gamma")
|
||||
convert.IntVar(&opts.LevelsInMax, "levels-inmax", 255, "Highlight input value")
|
||||
convert.IntVar(&opts.LevelsOutMin, "levels-outmin", 0, "Shadow output value")
|
||||
convert.IntVar(&opts.LevelsOutMax, "levels-outmax", 255, "Highlight output value")
|
||||
convert.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
|
||||
convert.IntVar(&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")
|
||||
convert.StringVar(&profile, "profile", "", profileUsage)
|
||||
convert.IntVar(&opts.Width, "width", base.Width, "Image width")
|
||||
convert.IntVar(&opts.Height, "height", base.Height, "Image height")
|
||||
convert.BoolVar(&opts.Fit, "fit", base.Fit, "Best fit for required width and height")
|
||||
convert.BoolVar(&opts.NoUpscale, "no-upscale", base.NoUpscale, "Do not upscale images already smaller than the requested width/height")
|
||||
convert.IntVar(&opts.DPI, "dpi", base.DPI, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the original resolution")
|
||||
convert.StringVar(&opts.Format, "format", base.Format, "Image format, valid values are jpeg, png, tiff, bmp, webp, avif, jxl")
|
||||
convert.StringVar(&opts.Archive, "archive", base.Archive, "Archive format, valid values are zip, tar")
|
||||
convert.IntVar(&opts.ZipLevel, "zip-level", base.ZipLevel, "ZIP compression level, 0 disables compression, 1-9 sets deflate level (1 fastest, 9 smallest), -1 uses the default")
|
||||
convert.IntVar(&opts.Quality, "quality", base.Quality, "Image quality")
|
||||
convert.IntVar(&opts.Effort, "effort", base.Effort, "Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default")
|
||||
convert.BoolVar(&opts.Lossless, "lossless", base.Lossless, "Lossless compression (webp, avif, jxl), ignores quality")
|
||||
convert.BoolVar(&opts.Combine, "combine", base.Combine, "Combine all inputs into a single archive")
|
||||
convert.StringVar(&opts.OutFile, "outfile", base.OutFile, "Output file name for --combine (default: first input + -combined)")
|
||||
convert.IntVar(&opts.Filter, "filter", base.Filter, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 5=Gaussian, 6=Lanczos")
|
||||
convert.BoolVar(&opts.NoCover, "no-cover", base.NoCover, "Do not convert the cover image")
|
||||
convert.BoolVar(&opts.NoRGB, "no-rgb", base.NoRGB, "Do not convert images that have RGB colorspace")
|
||||
convert.BoolVar(&opts.NoNonImage, "no-nonimage", base.NoNonImage, "Remove non-image files from the archive")
|
||||
convert.BoolVar(&opts.NoConvert, "no-convert", base.NoConvert, "Do not transform or convert images")
|
||||
convert.BoolVar(&opts.Grayscale, "grayscale", base.Grayscale, "Convert images to grayscale (monochromatic)")
|
||||
convert.IntVar(&opts.Rotate, "rotate", base.Rotate, "Rotate images, valid values are 0, 90, 180, 270")
|
||||
convert.IntVar(&opts.Brightness, "brightness", base.Brightness, "Adjust the brightness of the images, must be in the range (-100, 100)")
|
||||
convert.IntVar(&opts.Contrast, "contrast", base.Contrast, "Adjust the contrast of the images, must be in the range (-100, 100)")
|
||||
convert.StringVar(&opts.Suffix, "suffix", base.Suffix, "Add suffix to file basename")
|
||||
convert.StringVar(&opts.OutDir, "outdir", base.OutDir, "Output directory")
|
||||
convert.IntVar(&opts.Size, "size", base.Size, "Process only files larger than size (in MB)")
|
||||
convert.BoolVar(&opts.Recursive, "recursive", base.Recursive, "Process subdirectories recursively")
|
||||
convert.BoolVar(&opts.Quiet, "quiet", base.Quiet, "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.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")
|
||||
cover.IntVar(&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")
|
||||
cover.StringVar(&profile, "profile", "", profileUsage)
|
||||
cover.IntVar(&opts.Width, "width", base.Width, "Image width")
|
||||
cover.IntVar(&opts.Height, "height", base.Height, "Image height")
|
||||
cover.BoolVar(&opts.Fit, "fit", base.Fit, "Best fit for required width and height")
|
||||
cover.BoolVar(&opts.NoUpscale, "no-upscale", base.NoUpscale, "Do not upscale images already smaller than the requested width/height")
|
||||
cover.IntVar(&opts.DPI, "dpi", base.DPI, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the original resolution")
|
||||
cover.StringVar(&opts.Format, "format", base.Format, "Image format, valid values are jpeg, png, tiff, bmp, webp, avif")
|
||||
cover.IntVar(&opts.Quality, "quality", base.Quality, "Image quality")
|
||||
cover.IntVar(&opts.Effort, "effort", base.Effort, "Encoder speed/effort, format-specific (webp method 0-6, avif speed 0-10, jxl effort 1-10), -1 uses the format default")
|
||||
cover.BoolVar(&opts.Lossless, "lossless", base.Lossless, "Lossless compression (webp, avif, jxl), ignores quality")
|
||||
cover.IntVar(&opts.Filter, "filter", base.Filter, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 5=Gaussian, 6=Lanczos")
|
||||
cover.StringVar(&opts.OutDir, "outdir", base.OutDir, "Output directory")
|
||||
cover.IntVar(&opts.Size, "size", base.Size, "Process only files larger than size (in MB)")
|
||||
cover.BoolVar(&opts.Recursive, "recursive", base.Recursive, "Process subdirectories recursively")
|
||||
cover.BoolVar(&opts.Quiet, "quiet", base.Quiet, "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.IntVar(&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")
|
||||
thumbnail.StringVar(&profile, "profile", "", profileUsage)
|
||||
thumbnail.IntVar(&opts.Width, "width", base.Width, "Image width")
|
||||
thumbnail.IntVar(&opts.Height, "height", base.Height, "Image height")
|
||||
thumbnail.BoolVar(&opts.Fit, "fit", base.Fit, "Best fit for required width and height")
|
||||
thumbnail.BoolVar(&opts.NoUpscale, "no-upscale", base.NoUpscale, "Do not upscale images already smaller than the requested width/height")
|
||||
thumbnail.IntVar(&opts.DPI, "dpi", base.DPI, "Document rendering resolution in DPI (PDF, EPUB, etc.), 0 uses the original resolution")
|
||||
thumbnail.IntVar(&opts.Filter, "filter", base.Filter, "0=NearestNeighbor, 1=Box, 2=Linear, 3=MitchellNetravali, 4=CatmullRom, 5=Gaussian, 6=Lanczos")
|
||||
thumbnail.StringVar(&opts.OutDir, "outdir", base.OutDir, "Output directory")
|
||||
thumbnail.StringVar(&opts.OutFile, "outfile", base.OutFile, "Output file")
|
||||
thumbnail.IntVar(&opts.Size, "size", base.Size, "Process only files larger than size (in MB)")
|
||||
thumbnail.BoolVar(&opts.Recursive, "recursive", base.Recursive, "Process subdirectories recursively")
|
||||
thumbnail.BoolVar(&opts.Quiet, "quiet", base.Quiet, "Hide console output")
|
||||
|
||||
meta := flag.NewFlagSet("meta", flag.ExitOnError)
|
||||
meta.SortFlags = false
|
||||
meta.BoolVar(&opts.Cover, "cover", false, "Print cover name")
|
||||
meta.BoolVar(&opts.Comment, "comment", false, "Print zip comment")
|
||||
meta.StringVar(&opts.CommentBody, "comment-body", "", "Set zip comment")
|
||||
@@ -230,25 +284,34 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
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\n\n")
|
||||
convert.VisitAll(func(f *flag.Flag) {
|
||||
order := []string{"profile", "width", "height", "fit", "no-upscale", "dpi", "format", "archive", "zip-level", "quality", "effort", "lossless", "combine", "outfile", "filter", "no-cover", "no-rgb",
|
||||
"no-nonimage", "no-convert", "grayscale", "rotate", "brightness", "contrast", "suffix", "outdir", "size", "recursive", "quiet"}
|
||||
for _, name := range order {
|
||||
f := convert.Lookup(name)
|
||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||
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) {
|
||||
order = []string{"profile", "width", "height", "fit", "no-upscale", "dpi", "format", "quality", "effort", "lossless", "filter", "outdir", "size", "recursive", "quiet"}
|
||||
for _, name := range order {
|
||||
f := cover.Lookup(name)
|
||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||
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) {
|
||||
order = []string{"profile", "width", "height", "fit", "no-upscale", "dpi", "filter", "outdir", "outfile", "size", "recursive", "quiet"}
|
||||
for _, name := range order {
|
||||
f := thumbnail.Lookup(name)
|
||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||
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) {
|
||||
order = []string{"cover", "comment", "comment-body", "file-add", "file-remove"}
|
||||
for _, name := range order {
|
||||
f := meta.Lookup(name)
|
||||
fmt.Fprintf(os.Stderr, " --%s\n \t", f.Name)
|
||||
fmt.Fprintf(os.Stderr, "%v (default %q)\n", f.Usage, f.DefValue)
|
||||
})
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n version\n \tPrint version\n\n")
|
||||
}
|
||||
|
||||
@@ -265,27 +328,27 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
|
||||
switch os.Args[1] {
|
||||
case "convert":
|
||||
_ = convert.Parse(os.Args[2:])
|
||||
operands := parseArgs(convert, os.Args[2:])
|
||||
if !pipe {
|
||||
args = convert.Args()
|
||||
args = operands
|
||||
}
|
||||
case "cover":
|
||||
opts.Cover = true
|
||||
_ = cover.Parse(os.Args[2:])
|
||||
operands := parseArgs(cover, os.Args[2:])
|
||||
if !pipe {
|
||||
args = cover.Args()
|
||||
args = operands
|
||||
}
|
||||
case "thumbnail":
|
||||
opts.Thumbnail = true
|
||||
_ = thumbnail.Parse(os.Args[2:])
|
||||
operands := parseArgs(thumbnail, os.Args[2:])
|
||||
if !pipe {
|
||||
args = thumbnail.Args()
|
||||
args = operands
|
||||
}
|
||||
case "meta":
|
||||
opts.Meta = true
|
||||
_ = meta.Parse(os.Args[2:])
|
||||
operands := parseArgs(meta, os.Args[2:])
|
||||
if !pipe {
|
||||
args = meta.Args()
|
||||
args = operands
|
||||
}
|
||||
case "version":
|
||||
opts.Version = true
|
||||
@@ -300,7 +363,20 @@ func parseFlags() (cbconvert.Options, []string) {
|
||||
return opts, args
|
||||
}
|
||||
|
||||
// piped checks if we have a piped stdin.
|
||||
// parseArgs parses flags interspersed with file/dir operands.
|
||||
func parseArgs(fs *flag.FlagSet, args []string) []string {
|
||||
var operands []string
|
||||
|
||||
_ = fs.Parse(args)
|
||||
for fs.NArg() > 0 {
|
||||
operands = append(operands, fs.Arg(0))
|
||||
_ = fs.Parse(fs.Args()[1:])
|
||||
}
|
||||
|
||||
return operands
|
||||
}
|
||||
|
||||
// piped checks if we have piped stdin.
|
||||
func piped() bool {
|
||||
f, err := os.Stdin.Stat()
|
||||
if err != nil {
|
||||
@@ -314,7 +390,7 @@ func piped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// lines returns slice of lines from reader.
|
||||
// lines returns slice of lines from the reader.
|
||||
func lines(r io.Reader) []string {
|
||||
data := make([]string, 0)
|
||||
scanner := bufio.NewScanner(r)
|
||||
@@ -326,3 +402,229 @@ func lines(r io.Reader) []string {
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// configPath returns the IupConfig file the GUI writes, matching IUP's per-platform location for APPNAME "cbconvert".
|
||||
func configPath() (string, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
dir := os.Getenv("LocalAppData")
|
||||
if dir == "" {
|
||||
return "", errors.New("configPath: LocalAppData is not set")
|
||||
}
|
||||
|
||||
return filepath.Join(dir, "cbconvert", "config.cfg"), nil
|
||||
}
|
||||
|
||||
dir, err := os.UserConfigDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("configPath: %w", err)
|
||||
}
|
||||
|
||||
return filepath.Join(dir, "cbconvert", "config"), nil
|
||||
}
|
||||
|
||||
// parseINI reads a simple INI file into section -> key -> value.
|
||||
func parseINI(path string) (map[string]map[string]string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sections := make(map[string]map[string]string)
|
||||
var cur map[string]string
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
line := strings.TrimSpace(sc.Text())
|
||||
if line == "" || strings.HasPrefix(line, ";") || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||
cur = make(map[string]string)
|
||||
sections[line[1:len(line)-1]] = cur
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if cur == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if k, v, ok := strings.Cut(line, "="); ok {
|
||||
cur[strings.TrimSpace(k)] = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
|
||||
if err := sc.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sections, nil
|
||||
}
|
||||
|
||||
// defaultOptions returns the convert defaults used when no profile is loaded.
|
||||
func defaultOptions() cbconvert.Options {
|
||||
o := cbconvert.NewOptions()
|
||||
o.OutDir = "."
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// loadProfile reads the named GUI profile and translates its control values into Options.
|
||||
func loadProfile(name string) (cbconvert.Options, error) {
|
||||
o := defaultOptions()
|
||||
|
||||
path, err := configPath()
|
||||
if err != nil {
|
||||
return o, fmt.Errorf("loadProfile: %w", err)
|
||||
}
|
||||
|
||||
ini, err := parseINI(path)
|
||||
if err != nil {
|
||||
return o, fmt.Errorf("loadProfile: %w", err)
|
||||
}
|
||||
|
||||
sec, ok := ini["Profile:"+name]
|
||||
if !ok {
|
||||
return o, fmt.Errorf("loadProfile: profile %q not found in %s%s", name, path, knownProfiles(ini))
|
||||
}
|
||||
|
||||
str := func(key string, set func(string)) {
|
||||
if v, ok := sec[key]; ok {
|
||||
set(v)
|
||||
}
|
||||
}
|
||||
boolean := func(key string, set func(bool)) {
|
||||
if v, ok := sec[key]; ok {
|
||||
set(v == "1")
|
||||
}
|
||||
}
|
||||
integer := func(key string, set func(int)) {
|
||||
if v, ok := sec[key]; ok {
|
||||
if n, err := strconv.Atoi(v); err == nil {
|
||||
set(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
integer("Width", func(n int) { o.Width = n })
|
||||
integer("Height", func(n int) { o.Height = n })
|
||||
boolean("Fit", func(b bool) { o.Fit = b })
|
||||
boolean("NoUpscale", func(b bool) { o.NoUpscale = b })
|
||||
str("DPI", func(v string) { o.DPI = dpiFromString(v) })
|
||||
str("Format", func(v string) { o.Format = formatFromIndex(v) })
|
||||
str("Archive", func(v string) { o.Archive = archiveFromIndex(v) })
|
||||
str("ZipLevel", func(v string) { o.ZipLevel = zipLevelFromIndex(v) })
|
||||
integer("Quality", func(n int) { o.Quality = n })
|
||||
integer("Effort", func(n int) { o.Effort = n })
|
||||
boolean("Lossless", func(b bool) { o.Lossless = b })
|
||||
boolean("Combine", func(b bool) { o.Combine = b })
|
||||
integer("Filter", func(n int) { o.Filter = n - 1 })
|
||||
boolean("NoCover", func(b bool) { o.NoCover = b })
|
||||
boolean("NoRGB", func(b bool) { o.NoRGB = b })
|
||||
boolean("NoNonImage", func(b bool) { o.NoNonImage = b })
|
||||
boolean("NoConvert", func(b bool) { o.NoConvert = b })
|
||||
boolean("Grayscale", func(b bool) { o.Grayscale = b })
|
||||
str("Rotate", func(v string) { o.Rotate = rotateFromIndex(v) })
|
||||
integer("Brightness", func(n int) { o.Brightness = n })
|
||||
integer("Contrast", func(n int) { o.Contrast = n })
|
||||
str("Suffix", func(v string) { o.Suffix = v })
|
||||
str("OutDir", func(v string) { o.OutDir = v })
|
||||
integer("Size", func(n int) { o.Size = n })
|
||||
boolean("Recursive", func(b bool) { o.Recursive = b })
|
||||
|
||||
// Effort is format-specific in the GUI: only webp/avif/jxl use the slider, others fall back to the format default.
|
||||
switch o.Format {
|
||||
case "webp", "avif", "jxl":
|
||||
default:
|
||||
o.Effort = -1
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// knownProfiles lists the profile names from the config, for a helpful "not found" message.
|
||||
func knownProfiles(ini map[string]map[string]string) string {
|
||||
if p, ok := ini["Profiles"]; ok {
|
||||
if names := p["Names"]; names != "" {
|
||||
return "\navailable profiles: " + strings.ReplaceAll(names, ";", ", ")
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// profileArg extracts the --profile value from args, since it must be known before flag defaults are built.
|
||||
func profileArg(args []string) string {
|
||||
for i := 0; i < len(args); i++ {
|
||||
if args[i] == "--profile" || args[i] == "-profile" {
|
||||
if i+1 < len(args) {
|
||||
return args[i+1]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, pfx := range []string{"--profile=", "-profile="} {
|
||||
if v, ok := strings.CutPrefix(args[i], pfx); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// The index translations below mirror the GUI dropdown encodings stored in the profile.
|
||||
|
||||
func dpiFromString(s string) int {
|
||||
n, err := strconv.Atoi(strings.TrimSpace(s))
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
var profileFormats = []string{"jpeg", "png", "tiff", "bmp", "webp", "avif", "jxl"}
|
||||
|
||||
func formatFromIndex(s string) string {
|
||||
if i, _ := strconv.Atoi(s); i >= 1 && i <= len(profileFormats) {
|
||||
return profileFormats[i-1]
|
||||
}
|
||||
|
||||
return "jpeg"
|
||||
}
|
||||
|
||||
func archiveFromIndex(s string) string {
|
||||
if s == "2" {
|
||||
return "tar"
|
||||
}
|
||||
|
||||
return "zip"
|
||||
}
|
||||
|
||||
func zipLevelFromIndex(s string) int {
|
||||
switch i, _ := strconv.Atoi(s); i {
|
||||
case 1:
|
||||
return -1
|
||||
case 2:
|
||||
return 0
|
||||
default:
|
||||
return i - 2
|
||||
}
|
||||
}
|
||||
|
||||
func rotateFromIndex(s string) int {
|
||||
switch s {
|
||||
case "2":
|
||||
return 90
|
||||
case "3":
|
||||
return 180
|
||||
case "4":
|
||||
return 270
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProfileArg(t *testing.T) {
|
||||
cases := []struct {
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"--profile", "webp", "a.cbz"}, "webp"},
|
||||
{[]string{"--profile=webp", "a.cbz"}, "webp"},
|
||||
{[]string{"-profile", "webp"}, "webp"},
|
||||
{[]string{"-profile=webp"}, "webp"},
|
||||
{[]string{"--width", "800", "--profile", "x", "a.cbz"}, "x"},
|
||||
{[]string{"--width", "800", "a.cbz"}, ""},
|
||||
{[]string{"--profile"}, ""},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if got := profileArg(c.args); got != c.want {
|
||||
t.Errorf("profileArg(%v) = %q, want %q", c.args, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexTranslations(t *testing.T) {
|
||||
if got := formatFromIndex("5"); got != "webp" {
|
||||
t.Errorf("formatFromIndex(5) = %q, want webp", got)
|
||||
}
|
||||
if got := formatFromIndex("1"); got != "jpeg" {
|
||||
t.Errorf("formatFromIndex(1) = %q, want jpeg", got)
|
||||
}
|
||||
if got := formatFromIndex("99"); got != "jpeg" {
|
||||
t.Errorf("formatFromIndex(99) = %q, want jpeg fallback", got)
|
||||
}
|
||||
if got := archiveFromIndex("2"); got != "tar" {
|
||||
t.Errorf("archiveFromIndex(2) = %q, want tar", got)
|
||||
}
|
||||
if got := archiveFromIndex("1"); got != "zip" {
|
||||
t.Errorf("archiveFromIndex(1) = %q, want zip", got)
|
||||
}
|
||||
|
||||
zip := map[string]int{"1": -1, "2": 0, "3": 1, "11": 9}
|
||||
for in, want := range zip {
|
||||
if got := zipLevelFromIndex(in); got != want {
|
||||
t.Errorf("zipLevelFromIndex(%s) = %d, want %d", in, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
rot := map[string]int{"1": 0, "2": 90, "3": 180, "4": 270}
|
||||
for in, want := range rot {
|
||||
if got := rotateFromIndex(in); got != want {
|
||||
t.Errorf("rotateFromIndex(%s) = %d, want %d", in, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
if got := dpiFromString("Default"); got != 0 {
|
||||
t.Errorf("dpiFromString(Default) = %d, want 0", got)
|
||||
}
|
||||
if got := dpiFromString("150"); got != 150 {
|
||||
t.Errorf("dpiFromString(150) = %d, want 150", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseINI(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "config")
|
||||
data := "[Profiles]\nNames=Default;webp\n\n[Profile:webp]\nFormat=5\nQuality=90\n"
|
||||
if err := os.WriteFile(path, []byte(data), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ini, err := parseINI(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if ini["Profile:webp"]["Format"] != "5" {
|
||||
t.Errorf("Format = %q, want 5", ini["Profile:webp"]["Format"])
|
||||
}
|
||||
if ini["Profiles"]["Names"] != "Default;webp" {
|
||||
t.Errorf("Names = %q", ini["Profiles"]["Names"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadProfile(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("config path override via XDG_CONFIG_HOME is Linux-specific")
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(dir, "cbconvert"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data := "[Profile:webp]\nFormat=5\nQuality=90\nEffort=4\nArchive=2\nWidth=800\nFit=1\nFilter=7\nRotate=2\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "cbconvert", "config"), []byte(data), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("XDG_CONFIG_HOME", dir)
|
||||
|
||||
o, err := loadProfile("webp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if o.Format != "webp" {
|
||||
t.Errorf("Format = %q, want webp", o.Format)
|
||||
}
|
||||
if o.Quality != 90 {
|
||||
t.Errorf("Quality = %d, want 90", o.Quality)
|
||||
}
|
||||
if o.Effort != 4 {
|
||||
t.Errorf("Effort = %d, want 4 (webp keeps the slider value)", o.Effort)
|
||||
}
|
||||
if o.Archive != "tar" {
|
||||
t.Errorf("Archive = %q, want tar", o.Archive)
|
||||
}
|
||||
if o.Width != 800 || !o.Fit {
|
||||
t.Errorf("Width/Fit = %d/%v, want 800/true", o.Width, o.Fit)
|
||||
}
|
||||
if o.Filter != 6 {
|
||||
t.Errorf("Filter = %d, want 6 (GUI index 7 - 1)", o.Filter)
|
||||
}
|
||||
if o.Rotate != 90 {
|
||||
t.Errorf("Rotate = %d, want 90", o.Rotate)
|
||||
}
|
||||
|
||||
if _, err := loadProfile("missing"); err == nil {
|
||||
t.Error("loadProfile(missing) should error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadProfileEffortGate(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip("config path override via XDG_CONFIG_HOME is Linux-specific")
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(dir, "cbconvert"), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Format=1 (jpeg) with a stored Effort must collapse to -1, mirroring the GUI.
|
||||
data := "[Profile:jpeg]\nFormat=1\nEffort=4\n"
|
||||
if err := os.WriteFile(filepath.Join(dir, "cbconvert", "config"), []byte(data), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("XDG_CONFIG_HOME", dir)
|
||||
|
||||
o, err := loadProfile("jpeg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if o.Effort != -1 {
|
||||
t.Errorf("Effort = %d, want -1 for non-effort format", o.Effort)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GLIBC_x86_64="/usr/x86_64-pc-linux-gnu-static"
|
||||
MINGW_x86_64="/usr/x86_64-w64-mingw32"
|
||||
MACOS_x86_64="/usr/x86_64-apple-darwin"
|
||||
MACOS_aarch64="/usr/aarch64-apple-darwin"
|
||||
|
||||
VERSION="$(git --git-dir ../../.git describe --tags --abbrev=0 2>/dev/null || echo '1.0.0')"
|
||||
|
||||
BUILDDIR="cbconvert-${VERSION}"
|
||||
|
||||
mkdir -p "${BUILDDIR}"
|
||||
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 -X main.appVersion=${VERSION} '-extldflags=-static'" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && tar -czf "${BUILDDIR}-linux-x86_64.tar.gz" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
mkdir -p "${BUILDDIR}"
|
||||
CC=x86_64-w64-mingw32-gcc \
|
||||
PKG_CONFIG="/usr/bin/x86_64-w64-mingw32-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MINGW_x86_64/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MINGW_x86_64/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MINGW_x86_64/usr/include" \
|
||||
CGO_LDFLAGS="-L$MINGW_x86_64/usr/lib -ljxl -ljxl_dec -ljxl_profiler -ljxl_threads" \
|
||||
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o "${BUILDDIR}"/cbconvert.exe -ldflags "-s -w -X main.appVersion=${VERSION} '-extldflags=-static -Wl,--allow-multiple-definition'" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && zip -rq "${BUILDDIR}-windows-x86_64.zip" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1
|
||||
mkdir -p "${BUILDDIR}"
|
||||
PATH=${PATH}:${MACOS_x86_64}/bin \
|
||||
CC=x86_64-apple-darwin21.1-clang \
|
||||
PKG_CONFIG="x86_64-apple-darwin21.1-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MACOS_x86_64/usr/include -I$MACOS_x86_64/macports/pkgs/opt/local/include" \
|
||||
CGO_LDFLAGS="-L$MACOS_x86_64/SDK/MacOSX12.1.sdk/usr/lib -L$MACOS_x86_64/macports/pkgs/opt/local/lib -mmacosx-version-min=10.15" \
|
||||
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o "${BUILDDIR}"/cbconvert -ldflags "-linkmode external -s -w -X main.appVersion=${VERSION}" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && zip -rq "${BUILDDIR}-darwin-x86_64.zip" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
|
||||
export OSXCROSS_PKG_CONFIG_USE_NATIVE_VARIABLES=1
|
||||
mkdir -p "${BUILDDIR}"
|
||||
PATH=${PATH}:${MACOS_aarch64}/bin \
|
||||
CC=aarch64-apple-darwin21.1-clang \
|
||||
PKG_CONFIG="aarch64-apple-darwin21.1-pkg-config" \
|
||||
PKG_CONFIG_PATH="$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
PKG_CONFIG_LIBDIR="$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib/pkgconfig" \
|
||||
CGO_CFLAGS="-I$MACOS_aarch64/usr/include -I$MACOS_aarch64/macports/pkgs/opt/local/include" \
|
||||
CGO_LDFLAGS="-L$MACOS_aarch64/SDK/MacOSX12.1.sdk/usr/lib -L$MACOS_aarch64/macports/pkgs/opt/local/lib -mmacosx-version-min=10.15" \
|
||||
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 \
|
||||
go build -trimpath -tags 'extlib pkgconfig' -v -o "${BUILDDIR}"/cbconvert -ldflags "-linkmode external -s -w -X main.appVersion=${VERSION}" && \
|
||||
cp ../../README.md ../../AUTHORS ../../COPYING "${BUILDDIR}" && zip -rq "${BUILDDIR}-darwin-aarch64.zip" "${BUILDDIR}"
|
||||
rm -rf "${BUILDDIR}"
|
||||
@@ -1,15 +1,52 @@
|
||||
module github.com/gen2brain/cbconvert
|
||||
|
||||
go 1.21
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/chai2010/webp v1.1.1
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/anthonynsimon/bild v0.15.0
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/fvbommel/sortorder v1.1.0
|
||||
github.com/gen2brain/go-fitz v1.23.1
|
||||
github.com/gen2brain/go-unarr v0.1.7
|
||||
golang.org/x/image v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
gopkg.in/gographics/imagick.v3 v3.4.3
|
||||
github.com/gen2brain/avif v0.5.1
|
||||
github.com/gen2brain/go-fitz v1.28.1
|
||||
github.com/gen2brain/jpegli v0.4.1
|
||||
github.com/gen2brain/jpegxl v0.5.2
|
||||
github.com/gen2brain/webp v0.6.1
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25
|
||||
github.com/mholt/archives v0.1.5
|
||||
golang.org/x/image v0.43.0
|
||||
golang.org/x/sync v0.21.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.1 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.4 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/ebitengine/purego v0.10.1 // indirect
|
||||
github.com/gen2brain/jpegn v0.4.2
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.1.1 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/stangelandcl/ppmd v0.1.1 // indirect
|
||||
github.com/tetratelabs/wazero v1.12.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,50 +1,141 @@
|
||||
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/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro=
|
||||
github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/anthonynsimon/bild v0.15.0 h1:FzvaNLuNlAPKw1Xz7V2WYOcGIEBMj8Y6ZyAk7CI+HzA=
|
||||
github.com/anthonynsimon/bild v0.15.0/go.mod h1:qIgJ9FldkCn0iy5Ad24fzUkz5R+iJ0WfhiV+6FeCB5A=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.4 h1:iHiVJfxbrB6RF4X+snI2MpVgNBKmVfGaTqZGNlMQIU0=
|
||||
github.com/bodgit/sevenzip v1.6.4/go.mod h1:ZtNi5KNgHXeXg1G7WiF0IWSuFE2eG6lt/cTGlvuirO0=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
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/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200604193436-ca8584a0e1c4/go.mod h1:9EXlPeHfblFFnwu5UOqmP2eoZfJyAZ2Ri/Vki33ajO0=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d h1:yeH8wrJa3+8uKKDAdURHUK1ds2UvKhMqX2MiOdVeKPs=
|
||||
github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d/go.mod h1:oKrjk2kb3rAR5NbtSTLUMvMSbc+k8ZosI3MaVH47noc=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210512043655-120bcdb2a55e/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
|
||||
github.com/dsoprea/go-logging v0.0.0-20190624164917-c4f10aab7696/go.mod h1:Nm/x2ZUNRW6Fe5C3LxdY1PyZY5wmDv/s5dkPJ/VB3iA=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200517223158-a10564966e9d/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd h1:l+vLbuxptsC6VQyQsfD7NnEC8BZuFpz45PgY+pH8YTg=
|
||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd/go.mod h1:7I+3Pe2o/YSU88W0hWlm9S22W7XI1JFNJ86U0zPKMf8=
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d h1:8+qI8ant/vZkNSsbwSjIR6XJfWcDVTg/qx/3pRUUZNA=
|
||||
github.com/dsoprea/go-png-image-structure v0.0.0-20210512210324-29b889a6093d/go.mod h1:yTR3tKgyk20phAFg6IE9ulMA5NjEDD2wyx+okRFLVtw=
|
||||
github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3z2L0mqsVYd6yveIv1lmtT3tcQQ3dVakPySffW8=
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4=
|
||||
github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
|
||||
github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
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.23.1 h1:x69/szWZXpI3jZ57mMqCg7WqqvtYnQG0lXts3L6M1Fc=
|
||||
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/go.mod h1:MK9a3hddpaIxjEtrE1f/LA5yJ7gA34cS7Oyr325sY9s=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/gen2brain/avif v0.5.1 h1:LQzLsJpWyGlsa4wuZ3D57qEbCiICIK7Yidz5ZPEwzTk=
|
||||
github.com/gen2brain/avif v0.5.1/go.mod h1:QgrYqdVE9y40PCfArK9VakcMIpYeDYpZmCSLkW6C1n8=
|
||||
github.com/gen2brain/go-fitz v1.28.1 h1:ToEYb2vN4ByaL2VmRNGk92Sa1UAkCn8bsObpA3WkQ48=
|
||||
github.com/gen2brain/go-fitz v1.28.1/go.mod h1:pY2hqAjp9Zy7qfPI2gwbJMHBFAdZpVXOLrRxD82l3Bs=
|
||||
github.com/gen2brain/jpegli v0.4.1 h1:qc11IQU0jTYFltroulT4MXmbu9YRftqHV0YBZ0Bqz5o=
|
||||
github.com/gen2brain/jpegli v0.4.1/go.mod h1:zJ++s4symmKCN1CLkrY0dGXTY3s0NWbd94Rz9KLdCzk=
|
||||
github.com/gen2brain/jpegn v0.4.2 h1:sxy2yolV1eNA02uYtnqBFm4EIC3ETnars98aG7Dc4LM=
|
||||
github.com/gen2brain/jpegn v0.4.2/go.mod h1:YvcVOmVPSAsefH6yn9HBW3uY0EHlZwCMoiJXoAWfgL0=
|
||||
github.com/gen2brain/jpegxl v0.5.2 h1:1ou9YRziU8PbpkfFJIyxrNjYM+WaMl2n9LloABxkKsU=
|
||||
github.com/gen2brain/jpegxl v0.5.2/go.mod h1:Wlc6lqx03RJfhiQRyHa2e+8VQwT4/qv7zSRsNv9T+yE=
|
||||
github.com/gen2brain/webp v0.6.1 h1:ei7Y1SWpQcdqz3YNDNyn4y2nQanxs9WLzwW5/2DKS64=
|
||||
github.com/gen2brain/webp v0.6.1/go.mod h1:iGWMaCSw7t3I/Cv9llzEKmpnR36S8lS8VL/ZVjxU0JE=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537 h1:KeIaDS/+VEy/bhDYjG3Z78dOyLAU4HXcVxmd0WYHJTE=
|
||||
github.com/golang/geo v0.0.0-20260625163123-7c0e84413537/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25 h1:YLvr1eE6cdCqjOe972w/cYF+FjW34v27+9Vo5106B4M=
|
||||
github.com/jsummers/gobmp v0.0.0-20230614200233-a9de23ed2e25/go.mod h1:kLgvv7o6UM+0QSf0QjAse3wReFDsb9qbZJdfexWlrQw=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/minlz v1.1.1 h1:OGmft1V6AnI/Wme332U6bhG54nxEan+VFgkD7lat4KM=
|
||||
github.com/minio/minlz v1.1.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/nwaples/rardecode/v2 v2.2.5 h1:L5doqgGfQwI7qADJMqnkrSB86rpPsqQDrHeO0HWa5JY=
|
||||
github.com/nwaples/rardecode/v2 v2.2.5/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
||||
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
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/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/stangelandcl/ppmd v0.1.1 h1:c25QazhlWUn5nmR1QOzafKhQxBicAr7GGCKER2aJ8H8=
|
||||
github.com/stangelandcl/ppmd v0.1.1/go.mod h1:Rrv7M+/2P5jYr/GMLhBl7Ug3uJ1bUiVzr5LbbaV6xgY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
|
||||
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
|
||||
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.12.0 h1:w13vZbU4o5rKOFFR8y7M+c4A5jXDC0uXTdHYRP8X2DQ=
|
||||
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.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.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
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.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-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.3 h1:9plKFE/Us913jBN6KohtLG9FNW8LPvfpjiGAORIiEHg=
|
||||
gopkg.in/gographics/imagick.v3 v3.4.3/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
|
||||
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
|
||||
cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
|
||||
cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0=
|
||||
cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
|
||||
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
|
||||
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
|
||||
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
|
||||
cloud.google.com/go/storage v1.56.0 h1:iixmq2Fse2tqxMbWhLWC9HfBj1qdxqAmiK8/eqtsLxI=
|
||||
cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f h1:Y8xYupdHxryycyPlc9Y+bSQAYZnetRJ70VMVKm5CKI0=
|
||||
github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780 h1:tFh1tRc4CA31yP6qDcu+Trax5wW5GuMxvkIba07qVLY=
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210512043655-120bcdb2a55e h1:E4XTSQZF/JtOQWcSaJBJho7t+RNWfdO92W/5skg10Jk=
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e h1:IxIbA7VbCNrwumIYjDoMOdf4KOSkMC6NJE4s8oRbE7E=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.35.0 h1:ixjkELDE+ru6idPxcHLj8LBVc2bFP7iBytj353BoHUo=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
|
||||
github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/google/go-cmdtest v0.4.0 h1:ToXh6W5spLp3npJV92tk6d5hIpUPYEzHLkD+rncbyhI=
|
||||
github.com/google/go-cmdtest v0.4.0/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
|
||||
github.com/google/go-units v0.0.0-20250612230646-eddd77f68220 h1:hM8xVjUr4Iv/iQIx4Jq1xckZkKlXu51Gqku5HlEpQAE=
|
||||
github.com/google/go-units v0.0.0-20250612230646-eddd77f68220/go.mod h1:wBcRMlRM/bVzYk9xtR2hOp3+iWOhEh1FiK8sAzeR9eA=
|
||||
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=
|
||||
github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
|
||||
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
||||
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
google.golang.org/api v0.259.0 h1:90TaGVIxScrh1Vn/XI2426kRpBqHwWIzVBzJsVZ5XrQ=
|
||||
google.golang.org/api v0.259.0/go.mod h1:LC2ISWGWbRoyQVpxGntWwLWN/vLNxxKBK9KuJRI8Te4=
|
||||
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934=
|
||||
google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||