Add File type

This commit is contained in:
Milan Nikolic
2023-08-29 20:07:35 +02:00
parent 50b0911586
commit fdfa80875e
5 changed files with 81 additions and 60 deletions

View File

@@ -8,7 +8,7 @@ It can convert comics to different formats to fit your various devices.
### Features ### Features
* reads RAR, ZIP, 7Z, CBR, CBZ, CB7, CBT, PDF, EPUB, MOBI and plain directory * reads RAR, ZIP, 7Z, TAR, CBR, CBZ, CB7, CBT, PDF, EPUB, MOBI and plain directory
* saves processed comics in CBZ (ZIP) archive format or CBT (TAR) * saves processed comics in CBZ (ZIP) archive format or CBT (TAR)
* images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, or 4-Bit BMP (16 colors) file format * images can be converted to JPEG, PNG, TIFF, WEBP, AVIF, or 4-Bit BMP (16 colors) file format
* rotate, flip, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images * rotate, flip, adjust brightness/contrast, adjust levels (Photoshop-like) or grayscale images

View File

@@ -30,6 +30,7 @@ import (
"golang.org/x/image/tiff" "golang.org/x/image/tiff"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/dustin/go-humanize"
"github.com/fvbommel/sortorder" "github.com/fvbommel/sortorder"
"github.com/gen2brain/go-fitz" "github.com/gen2brain/go-fitz"
"github.com/gen2brain/go-unarr" "github.com/gen2brain/go-unarr"
@@ -108,9 +109,9 @@ type Options struct {
// Remove file // Remove file
FileRemove string FileRemove string
// Output file // Output file
Outfile string OutFile string
// Output directory // Output directory
Outdir string OutDir string
// Convert images to grayscale (monochromatic) // Convert images to grayscale (monochromatic)
Grayscale bool Grayscale bool
// Rotate images, valid values are 0, 90, 180, 270 // Rotate images, valid values are 0, 90, 180, 270
@@ -118,25 +119,25 @@ type Options struct {
// Flip images, valid values are none, horizontal, vertical // Flip images, valid values are none, horizontal, vertical
Flip string Flip string
// Adjust the brightness of the images, must be in the range (-100, 100) // Adjust the brightness of the images, must be in the range (-100, 100)
Brightness float64 Brightness int
// Adjust the contrast of the images, must be in the range (-100, 100) // Adjust the contrast of the images, must be in the range (-100, 100)
Contrast float64 Contrast int
// Process subdirectories recursively // Process subdirectories recursively
Recursive bool Recursive bool
// Process only files larger than size (in MB) // Process only files larger than size (in MB)
Size int64 Size int
// Hide console output // Hide console output
Quiet bool Quiet bool
// Shadow input value // Shadow input value
LevelsInMin float64 LevelsInMin int
// Highlight input value // Highlight input value
LevelsInMax float64 LevelsInMax int
// Midpoint/gamma // Midpoint/gamma
LevelsGamma float64 LevelsGamma float64
// Shadow output value // Shadow output value
LevelsOutMin float64 LevelsOutMin int
// Highlight output value // Highlight output value
LevelsOutMax float64 LevelsOutMax int
} }
// Convertor type. // Convertor type.
@@ -161,6 +162,14 @@ type Convertor struct {
OnCompress func() OnCompress func()
} }
// File type.
type File struct {
Name string
Path string
Size int64
SizeHuman string
}
// New returns new convertor. // New returns new convertor.
func New(o Options) *Convertor { func New(o Options) *Convertor {
c := &Convertor{} c := &Convertor{}
@@ -501,11 +510,11 @@ func (c *Convertor) imageTransform(img image.Image) image.Image {
} }
if c.Opts.Brightness != 0 { if c.Opts.Brightness != 0 {
i = imaging.AdjustBrightness(i, c.Opts.Brightness) i = imaging.AdjustBrightness(i, float64(c.Opts.Brightness))
} }
if c.Opts.Contrast != 0 { if c.Opts.Contrast != 0 {
i = imaging.AdjustContrast(i, c.Opts.Contrast) i = imaging.AdjustContrast(i, float64(c.Opts.Contrast))
} }
return i return i
@@ -525,16 +534,16 @@ func (c *Convertor) imageLevel(img image.Image) (image.Image, error) {
_, qrange := imagick.GetQuantumRange() _, qrange := imagick.GetQuantumRange()
quantumRange := float64(qrange) quantumRange := float64(qrange)
inmin := (quantumRange * c.Opts.LevelsInMin) / 255 inMin := (quantumRange * float64(c.Opts.LevelsInMin)) / 255
inmax := (quantumRange * c.Opts.LevelsInMax) / 255 inMax := (quantumRange * float64(c.Opts.LevelsInMax)) / 255
outmin := (quantumRange * c.Opts.LevelsOutMin) / 255 outMin := (quantumRange * float64(c.Opts.LevelsOutMin)) / 255
outmax := (quantumRange * c.Opts.LevelsOutMax) / 255 outMax := (quantumRange * float64(c.Opts.LevelsOutMax)) / 255
if err := mw.LevelImage(inmin, c.Opts.LevelsGamma, inmax); err != nil { if err := mw.LevelImage(inMin, c.Opts.LevelsGamma, inMax); err != nil {
return img, fmt.Errorf("imageLevel: %w", err) return img, fmt.Errorf("imageLevel: %w", err)
} }
if err := mw.LevelImage(-outmin, 1.0, quantumRange+(quantumRange-outmax)); err != nil { if err := mw.LevelImage(-outMin, 1.0, quantumRange+(quantumRange-outMax)); err != nil {
return img, fmt.Errorf("imageLevel: %w", err) return img, fmt.Errorf("imageLevel: %w", err)
} }
@@ -752,14 +761,14 @@ func (c *Convertor) archiveSaveZip(fileName string) error {
var zipName string var zipName string
if c.Opts.Recursive { if c.Opts.Recursive {
err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755)
if err != nil { if err != nil {
return fmt.Errorf("archiveSaveZip: %w", err) return fmt.Errorf("archiveSaveZip: %w", err)
} }
zipName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix)) zipName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix))
} else { } else {
zipName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix)) zipName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbz", c.baseNoExt(fileName), c.Opts.Suffix))
} }
zipFile, err := os.Create(zipName) zipFile, err := os.Create(zipName)
@@ -826,14 +835,14 @@ func (c *Convertor) archiveSaveTar(fileName string) error {
var tarName string var tarName string
if c.Opts.Recursive { if c.Opts.Recursive {
err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755)
if err != nil { if err != nil {
return fmt.Errorf("archiveSaveTar: %w", err) return fmt.Errorf("archiveSaveTar: %w", err)
} }
tarName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix)) tarName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix))
} else { } else {
tarName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix)) tarName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s%s.cbt", c.baseNoExt(fileName), c.Opts.Suffix))
} }
tarFile, err := os.Create(tarName) tarFile, err := os.Create(tarName)
@@ -1416,7 +1425,7 @@ func (c *Convertor) isNonImage(f string) bool {
// isSize checks size of file. // isSize checks size of file.
func (c *Convertor) isSize(size int64) bool { func (c *Convertor) isSize(size int64) bool {
if c.Opts.Size > 0 { if c.Opts.Size > 0 {
if size < c.Opts.Size*(1024*1024) { if size < int64(c.Opts.Size)*(1024*1024) {
return false return false
} }
} }
@@ -1471,8 +1480,17 @@ func (c *Convertor) Terminate() {
} }
// Files returns list of found comic files. // Files returns list of found comic files.
func (c *Convertor) Files(args []string) ([]string, error) { func (c *Convertor) Files(args []string) ([]File, error) {
var files []string var files []File
toFile := func(fp string, f os.FileInfo) File {
var file File
file.Name = filepath.Base(fp)
file.Path = fp
file.Size = f.Size()
file.SizeHuman = humanize.IBytes(uint64(file.Size))
return file
}
walkFiles := func(fp string, f os.FileInfo, err error) error { walkFiles := func(fp string, f os.FileInfo, err error) error {
if f.IsDir() { if f.IsDir() {
@@ -1480,7 +1498,7 @@ func (c *Convertor) Files(args []string) ([]string, error) {
} }
if c.isArchive(fp) || c.isDocument(fp) { if c.isArchive(fp) || c.isDocument(fp) {
if c.isSize(f.Size()) { if c.isSize(f.Size()) {
files = append(files, fp) files = append(files, toFile(fp, f))
} }
} }
@@ -1502,7 +1520,7 @@ func (c *Convertor) Files(args []string) ([]string, error) {
} }
if count > 1 { if count > 1 {
files = append(files, fp) files = append(files, toFile(fp, f))
} }
} }
@@ -1523,7 +1541,7 @@ func (c *Convertor) Files(args []string) ([]string, error) {
if !stat.IsDir() { if !stat.IsDir() {
if c.isArchive(path) || c.isDocument(path) { if c.isArchive(path) || c.isDocument(path) {
if c.isSize(stat.Size()) { if c.isSize(stat.Size()) {
files = append(files, path) files = append(files, toFile(path, stat))
} }
} }
} else { } else {
@@ -1544,7 +1562,7 @@ func (c *Convertor) Files(args []string) ([]string, error) {
return files, fmt.Errorf("%s: %w", arg, err) return files, fmt.Errorf("%s: %w", arg, err)
} }
if c.isSize(info.Size()) { if c.isSize(info.Size()) {
files = append(files, filepath.Join(path, f.Name())) files = append(files, toFile(filepath.Join(path, f.Name()), info))
} }
} }
} }
@@ -1557,7 +1575,7 @@ func (c *Convertor) Files(args []string) ([]string, error) {
return files, fmt.Errorf("%s: %w", arg, err) return files, fmt.Errorf("%s: %w", arg, err)
} }
} else { } else {
files = append(files, path) files = append(files, toFile(path, stat))
} }
} }
} }
@@ -1587,14 +1605,14 @@ func (c *Convertor) Cover(fileName string, fileInfo os.FileInfo) error {
var fName string var fName string
if c.Opts.Recursive { if c.Opts.Recursive {
err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755)
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", fileName, err) return fmt.Errorf("%s: %w", fileName, err)
} }
fName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format)) fName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format))
} else { } else {
fName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format)) fName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%s.%s", c.baseNoExt(fileName), c.Opts.Format))
} }
switch c.Opts.Format { switch c.Opts.Format {
@@ -1642,21 +1660,21 @@ func (c *Convertor) Thumbnail(fileName string, info os.FileInfo) error {
var fName string var fName string
var fURI string var fURI string
if c.Opts.Outfile == "" { if c.Opts.OutFile == "" {
fURI = "file://" + fileName fURI = "file://" + fileName
if c.Opts.Recursive { if c.Opts.Recursive {
err := os.MkdirAll(filepath.Join(c.Opts.Outdir, filepath.Dir(fileName)), 0755) err := os.MkdirAll(filepath.Join(c.Opts.OutDir, filepath.Dir(fileName)), 0755)
if err != nil { if err != nil {
return fmt.Errorf("%s: %w", fileName, err) return fmt.Errorf("%s: %w", fileName, err)
} }
fName = filepath.Join(c.Opts.Outdir, filepath.Dir(fileName), fmt.Sprintf("%x.png", md5.Sum([]byte(fURI)))) fName = filepath.Join(c.Opts.OutDir, filepath.Dir(fileName), fmt.Sprintf("%x.png", md5.Sum([]byte(fURI))))
} else { } else {
fName = filepath.Join(c.Opts.Outdir, fmt.Sprintf("%x.png", md5.Sum([]byte(fURI)))) fName = filepath.Join(c.Opts.OutDir, fmt.Sprintf("%x.png", md5.Sum([]byte(fURI))))
} }
} else { } else {
abs, _ := filepath.Abs(c.Opts.Outfile) abs, _ := filepath.Abs(c.Opts.OutFile)
fURI = "file://" + abs fURI = "file://" + abs
fName = abs fName = abs
} }

View File

@@ -30,8 +30,8 @@ func main() {
} }
}() }()
if _, err := os.Stat(opts.Outdir); err != nil { if _, err := os.Stat(opts.OutDir); err != nil {
if err := os.MkdirAll(opts.Outdir, 0775); err != nil { if err := os.MkdirAll(opts.OutDir, 0775); err != nil {
fmt.Println(err) fmt.Println(err)
} }
os.Exit(1) os.Exit(1)
@@ -83,7 +83,7 @@ func main() {
} }
for _, file := range files { for _, file := range files {
stat, err := os.Stat(file) stat, err := os.Stat(file.Path)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@@ -91,7 +91,7 @@ func main() {
switch { switch {
case opts.Meta: case opts.Meta:
ret, err := conv.Meta(file) ret, err := conv.Meta(file.Path)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@@ -105,14 +105,14 @@ func main() {
continue continue
case opts.Cover: case opts.Cover:
if err := conv.Cover(file, stat); err != nil { if err := conv.Cover(file.Path, stat); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
continue continue
case opts.Thumbnail: case opts.Thumbnail:
if err = conv.Thumbnail(file, stat); err != nil { if err = conv.Thumbnail(file.Path, stat); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
@@ -120,7 +120,7 @@ func main() {
continue continue
} }
if err := conv.Convert(file, stat); err != nil { if err := conv.Convert(file.Path, stat); err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
@@ -151,16 +151,16 @@ func parseFlags() (cbconvert.Options, []string) {
convert.BoolVar(&opts.Grayscale, "grayscale", false, "Convert images to grayscale (monochromatic)") 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.Rotate, "rotate", 0, "Rotate images, valid values are 0, 90, 180, 270")
convert.StringVar(&opts.Flip, "flip", "none", "Flip images, valid values are none, horizontal, vertical") convert.StringVar(&opts.Flip, "flip", "none", "Flip images, valid values are none, horizontal, vertical")
convert.Float64Var(&opts.Brightness, "brightness", 0, "Adjust the brightness of the images, must be in the range (-100, 100)") convert.IntVar(&opts.Brightness, "brightness", 0, "Adjust the brightness of the images, must be in the range (-100, 100)")
convert.Float64Var(&opts.Contrast, "contrast", 0, "Adjust the contrast of the images, must be in the range (-100, 100)") convert.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.StringVar(&opts.Suffix, "suffix", "", "Add suffix to file basename")
convert.Float64Var(&opts.LevelsInMin, "levels-inmin", 0, "Shadow input value") convert.IntVar(&opts.LevelsInMin, "levels-inmin", 0, "Shadow input value")
convert.Float64Var(&opts.LevelsGamma, "levels-gamma", 1.0, "Midpoint/Gamma") convert.Float64Var(&opts.LevelsGamma, "levels-gamma", 1.0, "Midpoint/Gamma")
convert.Float64Var(&opts.LevelsInMax, "levels-inmax", 255, "Highlight input value") convert.IntVar(&opts.LevelsInMax, "levels-inmax", 255, "Highlight input value")
convert.Float64Var(&opts.LevelsOutMin, "levels-outmin", 0, "Shadow output value") convert.IntVar(&opts.LevelsOutMin, "levels-outmin", 0, "Shadow output value")
convert.Float64Var(&opts.LevelsOutMax, "levels-outmax", 255, "Highlight output value") convert.IntVar(&opts.LevelsOutMax, "levels-outmax", 255, "Highlight output value")
convert.StringVar(&opts.Outdir, "outdir", ".", "Output directory") convert.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
convert.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)") 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.Recursive, "recursive", false, "Process subdirectories recursively")
convert.BoolVar(&opts.Quiet, "quiet", false, "Hide console output") convert.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")
@@ -172,8 +172,8 @@ func parseFlags() (cbconvert.Options, []string) {
cover.StringVar(&opts.Format, "format", "jpeg", "Image format, valid values are jpeg, png, tiff, bmp, webp, avif") 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.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.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.StringVar(&opts.OutDir, "outdir", ".", "Output directory")
cover.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)") 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.Recursive, "recursive", false, "Process subdirectories recursively")
cover.BoolVar(&opts.Quiet, "quiet", false, "Hide console output") cover.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")
@@ -183,9 +183,9 @@ func parseFlags() (cbconvert.Options, []string) {
thumbnail.IntVar(&opts.Height, "height", 0, "Image height") thumbnail.IntVar(&opts.Height, "height", 0, "Image height")
thumbnail.BoolVar(&opts.Fit, "fit", false, "Best fit for required width and 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.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.OutDir, "outdir", ".", "Output directory")
thumbnail.StringVar(&opts.Outfile, "outfile", "", "Output file") thumbnail.StringVar(&opts.OutFile, "outfile", "", "Output file")
thumbnail.Int64Var(&opts.Size, "size", 0, "Process only files larger than size (in MB)") 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.Recursive, "recursive", false, "Process subdirectories recursively")
thumbnail.BoolVar(&opts.Quiet, "quiet", false, "Hide console output") thumbnail.BoolVar(&opts.Quiet, "quiet", false, "Hide console output")

1
go.mod
View File

@@ -5,6 +5,7 @@ go 1.21
require ( require (
github.com/chai2010/webp v1.1.1 github.com/chai2010/webp v1.1.1
github.com/disintegration/imaging v1.6.2 github.com/disintegration/imaging v1.6.2
github.com/dustin/go-humanize v1.0.1
github.com/fvbommel/sortorder v1.1.0 github.com/fvbommel/sortorder v1.1.0
github.com/gen2brain/go-fitz v1.23.1 github.com/gen2brain/go-fitz v1.23.1
github.com/gen2brain/go-unarr v0.1.7 github.com/gen2brain/go-unarr v0.1.7

2
go.sum
View File

@@ -2,6 +2,8 @@ github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= 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 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
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/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= 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/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 h1:x69/szWZXpI3jZ57mMqCg7WqqvtYnQG0lXts3L6M1Fc=