Remove ImageMagick dependency

This commit is contained in:
Milan Nikolic
2024-11-02 20:08:02 +01:00
parent 71d8f9dbb6
commit c0a077fcd1
6 changed files with 39 additions and 272 deletions

View File

@@ -191,10 +191,10 @@ This table maps quality settings for JPEG to the respective AVIF and WEBP qualit
### Compile
Install ImageMagick7 (with libheif/libjxl support) and MuPDF libraries and headers and then install to GOBIN:
Install to `GOBIN`:
`go install -tags extlib github.com/gen2brain/cbconvert/cmd/cbconvert@latest`
`go install 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`
`go install github.com/gen2brain/cbconvert/cmd/cbconvert-gui@latest`

View File

@@ -6,6 +6,10 @@ import (
"crypto/md5"
"errors"
"fmt"
"image"
_ "image/gif"
"image/jpeg"
"image/png"
"io"
"os"
"path/filepath"
@@ -15,12 +19,9 @@ import (
"strings"
"sync/atomic"
"image"
_ "image/gif" // allow gif decoding
"image/jpeg"
"image/png"
"git.sr.ht/~jackmordaunt/go-libwebp/webp"
"github.com/gen2brain/avif"
"github.com/gen2brain/jpegxl"
"github.com/gen2brain/webp"
"golang.org/x/image/tiff"
"github.com/disintegration/imaging"
@@ -30,7 +31,6 @@ import (
"github.com/gen2brain/go-fitz"
"github.com/gen2brain/go-unarr"
"golang.org/x/sync/errgroup"
"gopkg.in/gographics/imagick.v3/imagick"
)
// Resample filters.
@@ -310,11 +310,7 @@ func (c *Converter) convertArchive(ctx context.Context, fileName string) error {
var img image.Image
img, err = c.imageDecode(bytes.NewReader(data))
if err != nil {
e := err
img, err = c.imDecode(bytes.NewReader(data), pathName)
if err != nil {
return fmt.Errorf("convertArchive: %w: %w", e, err)
}
return fmt.Errorf("convertArchive: %w", err)
}
if c.Opts.NoRGB && !isGrayScale(img) {
@@ -406,16 +402,7 @@ func (c *Converter) convertDirectory(ctx context.Context, dirPath string) error
var i image.Image
i, err = c.imageDecode(file)
if err != nil {
e := err
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return fmt.Errorf("convertDirectory: %w: %w", e, err)
}
i, err = c.imDecode(file, img)
if err != nil {
return fmt.Errorf("convertDirectory: %w: %w", e, err)
}
return fmt.Errorf("convertDirectory: %w", err)
}
if c.Opts.NoRGB && !isGrayScale(i) {
@@ -480,15 +467,8 @@ func (c *Converter) imageConvert(ctx context.Context, img image.Image, index int
}
defer w.Close()
switch c.Opts.Format {
case "jpeg", "png", "tiff", "webp":
if err := c.imageEncode(img, w); err != nil {
return fmt.Errorf("imageConvert: %w", err)
}
case "bmp", "avif", "jxl":
if err := c.imEncode(img, w); err != nil {
return fmt.Errorf("imageConvert: %w", err)
}
if err := c.imageEncode(img, w); err != nil {
return fmt.Errorf("imageConvert: %w", err)
}
return nil
@@ -542,45 +522,6 @@ func (c *Converter) imageDecode(reader io.Reader) (image.Image, error) {
return img, nil
}
// imDecode decodes image from reader (ImageMagick).
func (c *Converter) imDecode(reader io.Reader, fileName string) (image.Image, error) {
mw := imagick.NewMagickWand()
defer mw.Destroy()
var img image.Image
b, err := io.ReadAll(reader)
if err != nil {
return img, fmt.Errorf("imDecode: %w", err)
}
if err = mw.SetFilename(fileName); err != nil {
return img, fmt.Errorf("imDecode: %w", err)
}
if err = mw.ReadImageBlob(b); err != nil {
return img, fmt.Errorf("imDecode: %w", err)
}
w := mw.GetImageWidth()
h := mw.GetImageHeight()
out, err := mw.ExportImagePixels(0, 0, w, h, "RGBA", imagick.PIXEL_CHAR)
if err != nil {
return img, fmt.Errorf("imDecode: %w", err)
}
data, ok := out.([]byte)
if ok {
rgba := image.NewRGBA(image.Rect(0, 0, int(w), int(h)))
rgba.Pix = data
img = rgba
}
return img, nil
}
// imageEncode encodes image to file.
func (c *Converter) imageEncode(img image.Image, w io.Writer) error {
var err error
@@ -593,7 +534,11 @@ func (c *Converter) imageEncode(img image.Image, w io.Writer) error {
case "jpeg":
err = jpeg.Encode(w, img, &jpeg.Options{Quality: c.Opts.Quality})
case "webp":
err = webp.Encode(w, img, webp.Quality(float32(c.Opts.Quality)))
err = webp.Encode(w, img, webp.Options{Quality: c.Opts.Quality, Method: webp.DefaultMethod})
case "avif":
err = avif.Encode(w, img, avif.Options{Quality: c.Opts.Quality, Speed: avif.DefaultSpeed})
case "jxl":
err = jpegxl.Encode(w, img, jpegxl.Options{Quality: c.Opts.Quality, Effort: jpegxl.DefaultEffort})
}
if err != nil {
@@ -603,82 +548,6 @@ func (c *Converter) imageEncode(img image.Image, w io.Writer) error {
return nil
}
// imEncode encodes image to file (ImageMagick).
func (c *Converter) imEncode(i image.Image, w io.Writer) error {
mw := imagick.NewMagickWand()
defer mw.Destroy()
if err := mw.ConstituteImage(uint(i.Bounds().Dx()), uint(i.Bounds().Dy()),
"RGBA", imagick.PIXEL_CHAR, imageToRGBA(i).Pix); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
switch c.Opts.Format {
case "png":
if err := mw.SetImageFormat("PNG"); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
case "tiff":
if err := mw.SetImageFormat("TIFF"); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
case "jpeg":
if err := mw.SetImageFormat("JPEG"); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
case "bmp":
pw := imagick.NewPixelWand()
pw.SetColor("black")
defer pw.Destroy()
if err := mw.SetImageFormat("BMP3"); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageBackgroundColor(pw); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_REMOVE); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageAlphaChannel(imagick.ALPHA_CHANNEL_DEACTIVATE); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageMatte(false); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageCompression(imagick.COMPRESSION_NO); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.QuantizeImage(16, mw.GetImageColorspace(), 1, imagick.DITHER_METHOD_NO, true); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
case "avif":
if err := mw.SetImageFormat("AVIF"); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
case "jxl":
if err := mw.SetImageFormat("JXL"); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
if err := mw.SetImageCompressionQuality(uint(c.Opts.Quality)); err != nil {
return fmt.Errorf("imEncode: %w", err)
}
}
_, err := w.Write(mw.GetImageBlob())
if err != nil {
return fmt.Errorf("imEncode: %w", err)
}
return nil
}
// coverArchive extracts cover from archive.
func (c *Converter) coverArchive(fileName string) (image.Image, error) {
var images []string
@@ -714,11 +583,7 @@ func (c *Converter) coverArchive(fileName string) (image.Image, error) {
var img image.Image
img, err = c.imageDecode(bytes.NewReader(data))
if err != nil {
e := err
img, err = c.imDecode(bytes.NewReader(data), cover)
if err != nil {
return nil, fmt.Errorf("coverArchive: %w: %w", e, err)
}
return nil, fmt.Errorf("coverArchive: %w", err)
}
return img, nil
@@ -759,16 +624,7 @@ func (c *Converter) coverDirectory(dir string) (image.Image, error) {
var img image.Image
img, err = c.imageDecode(file)
if err != nil {
e := err
_, err = file.Seek(0, io.SeekStart)
if err != nil {
return nil, fmt.Errorf("coverDirectory: %w: %w", e, err)
}
img, err = c.imDecode(file, cover)
if err != nil {
return nil, fmt.Errorf("coverDirectory: %w: %w", e, err)
}
return nil, fmt.Errorf("coverDirectory: %w", err)
}
return img, nil
@@ -830,16 +686,6 @@ func (c *Converter) coverImage(fileName string, fileInfo os.FileInfo) (image.Ima
return cover, nil
}
// Initialize inits ImageMagick.
func (c *Converter) Initialize() {
imagick.Initialize()
}
// Terminate terminates ImageMagick.
func (c *Converter) Terminate() {
imagick.Terminate()
}
// Cancel cancels the operation.
func (c *Converter) Cancel() {
if c.OnCancel != nil {
@@ -989,15 +835,8 @@ func (c *Converter) Cover(fileName string, fileInfo os.FileInfo) error {
}
defer w.Close()
switch c.Opts.Format {
case "jpeg", "png", "tiff", "webp":
if err := c.imageEncode(cover, w); err != nil {
return fmt.Errorf("%s: %w", fileName, err)
}
case "bmp", "avif", "jxl":
if err := c.imEncode(cover, w); err != nil {
return fmt.Errorf("%s: %w", fileName, err)
}
if err := c.imageEncode(cover, w); err != nil {
return fmt.Errorf("%s: %w", fileName, err)
}
return nil
@@ -1164,15 +1003,8 @@ func (c *Converter) Preview(fileName string, fileInfo os.FileInfo, width, height
var w bytes.Buffer
switch c.Opts.Format {
case "jpeg", "png", "tiff", "webp":
if err := c.imageEncode(i, &w); err != nil {
return img, fmt.Errorf("%s: %w", fileName, err)
}
case "bmp", "avif", "jxl":
if err := c.imEncode(i, &w); err != nil {
return img, fmt.Errorf("%s: %w", fileName, err)
}
if err := c.imageEncode(i, &w); err != nil {
return img, fmt.Errorf("%s: %w", fileName, err)
}
img.Width = i.Bounds().Dx()
@@ -1183,16 +1015,7 @@ func (c *Converter) Preview(fileName string, fileInfo os.FileInfo, width, height
dec, err := c.imageDecode(r)
if err != nil {
e := err
_, err = r.Seek(0, io.SeekStart)
if err != nil {
return img, fmt.Errorf("%s: %w: %w", fileName, e, err)
}
dec, err = c.imDecode(r, "cbc."+c.Opts.Format)
if err != nil {
return img, fmt.Errorf("%s: %w: %w", fileName, e, err)
}
return img, fmt.Errorf("%s: %w", fileName, err)
}
if width != 0 && height != 0 {

View File

@@ -260,9 +260,6 @@ func previewPost() {
go func(opts cbconvert.Options) {
conv := cbconvert.New(opts)
conv.Initialize()
defer conv.Terminate()
file := files[index]
img, err := conv.Preview(file.Path, file.Stat, width, height)
if err != nil {
@@ -765,9 +762,6 @@ func onThumbnail(ih iup.Ihandle) int {
iup.PostMessage(iup.GetHandle("ProgressBar"), "start", 0, conv)
go func(c *cbconvert.Converter) {
c.Initialize()
defer c.Terminate()
for _, file := range files {
if canceled {
break
@@ -811,9 +805,6 @@ func onCover(ih iup.Ihandle) int {
iup.PostMessage(iup.GetHandle("ProgressBar"), "start", 0, conv)
go func(c *cbconvert.Converter) {
c.Initialize()
defer c.Terminate()
for _, file := range files {
if canceled {
break
@@ -860,9 +851,6 @@ func onConvert(ih iup.Ihandle) int {
}))
go func(c *cbconvert.Converter) {
c.Initialize()
defer c.Terminate()
for _, file := range files {
if err := c.Convert(file.Path, file.Stat); err != nil {
if errors.Is(err, context.Canceled) {

View File

@@ -72,9 +72,6 @@ func main() {
os.Exit(1)
}
conv.Initialize()
defer conv.Terminate()
files, err := conv.Files(args)
if err != nil {
fmt.Println(err)

16
go.mod
View File

@@ -3,15 +3,17 @@ module github.com/gen2brain/cbconvert
go 1.23
require (
git.sr.ht/~jackmordaunt/go-libwebp v1.8.0
github.com/disintegration/imaging v1.6.2
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/avif v0.4.0
github.com/gen2brain/go-fitz v1.24.14
github.com/gen2brain/go-unarr v0.2.3
github.com/gen2brain/jpegxl v0.4.1
github.com/gen2brain/webp v0.5.0
golang.org/x/image v0.21.0
gopkg.in/gographics/imagick.v3 v3.5.1
golang.org/x/sync v0.8.0
)
require (
@@ -21,16 +23,8 @@ require (
github.com/ebitengine/purego v0.8.1 // indirect
github.com/go-errors/errors v1.1.1 // indirect
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/jupiterrider/ffi v0.2.1 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
github.com/tetratelabs/wazero v1.8.1 // indirect
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 // indirect
golang.org/x/sys v0.26.0 // indirect
gopkg.in/yaml.v2 v2.3.0 // indirect
modernc.org/libc v1.61.0 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
)

51
go.sum
View File

@@ -1,5 +1,3 @@
git.sr.ht/~jackmordaunt/go-libwebp v1.8.0 h1:05YWkouuQnR6CTPz/TlN0J9hXQiZDFk+bWeIbd73c7M=
git.sr.ht/~jackmordaunt/go-libwebp v1.8.0/go.mod h1:rXiwpxkkOe8OEvhh9g+HIvtPpBxgZXQyB6s+HXQUFyI=
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/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
@@ -21,10 +19,16 @@ github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/
github.com/ebitengine/purego v0.8.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/avif v0.4.0 h1:JuwAX2rVrkAzQrZx9lpIKx/ovCO35gCUquarfJ6uhHc=
github.com/gen2brain/avif v0.4.0/go.mod h1:oePci7KPleKZ8X/2rjZ3FlVm2JFYjPwXiQpNgq9wrzs=
github.com/gen2brain/go-fitz v1.24.14 h1:09weRkjVtLYNGo7l0J7DyOwBExbwi8SJ9h8YPhw9WEo=
github.com/gen2brain/go-fitz v1.24.14/go.mod h1:0KaZeQgASc20Yp5R/pFzyy7SmP01XcoHKNF842U2/S4=
github.com/gen2brain/go-unarr v0.2.3 h1:VwZg0P6Dc/8Uh51McjVhzUMg4wHwwbiyqjEFsFELc0c=
github.com/gen2brain/go-unarr v0.2.3/go.mod h1:hoHheVuf0KT8/hfvkEL7GMwj2h7fq0lF72NdyySdr3c=
github.com/gen2brain/jpegxl v0.4.1 h1:jWaVp5GkXLJXjRvC+g0R9+uo+xoWiS7DYKxJ3n5gHL4=
github.com/gen2brain/jpegxl v0.4.1/go.mod h1:zIIDnzh7WqG+z66zyzLWQ0M4AS5xi//pyJLgu32GB1o=
github.com/gen2brain/webp v0.5.0 h1:nn3o0BtKltoFKX9rlDZG/Y/aWqNzUZVyXdB815yVNfU=
github.com/gen2brain/webp v0.5.0/go.mod h1:Nb3xO5sy6MeUAHhru9H3GT7nlOQO5dKRNNlE92CZrJw=
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 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
@@ -32,25 +36,15 @@ github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
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/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jupiterrider/ffi v0.2.1 h1:08GJVDqz4eoQq7cKT1T0kwb9MB58XEAGjgxDvz80yBs=
github.com/jupiterrider/ffi v0.2.1/go.mod h1:tJ7Q8p/3blFjdWt5qJU4W5oDE0xloImvrViE+0td0Rk=
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/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550=
github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
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=
@@ -61,38 +55,9 @@ golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
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/gographics/imagick.v3 v3.5.1 h1:58JqK0UCx5RfvbRggF5FKuK6jHwAtTQopUxK8mzFa40=
gopkg.in/gographics/imagick.v3 v3.5.1/go.mod h1:+Q9nyA2xRZXrDyTtJ/eko+8V/5E7bWYs08ndkZp8UmA=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.21.0 h1:kKPI3dF7RIag8YcToh5ZwDcVMIv6VGa0ED5cvh0LMW4=
modernc.org/ccgo/v4 v4.21.0/go.mod h1:h6kt6H/A2+ew/3MW/p6KEoQmrq/i3pr0J/SiwiaF/g0=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.5.0 h1:bJ9ChznK1L1mUtAQtxi0wi5AtAs5jQuw4PrPHO5pb6M=
modernc.org/gc/v2 v2.5.0/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.61.0 h1:eGFcvWpqlnoGwzZeZe3PWJkkKbM/3SUGyk1DVZQ0TpE=
modernc.org/libc v1.61.0/go.mod h1:DvxVX89wtGTu+r72MLGhygpfi3aUGgZRdAYGCAVVud0=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=