Files
cbconvert/cbconvert_cover.go
T
2026-06-25 11:03:27 +02:00

269 lines
6.2 KiB
Go

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 := doc.ImageDPI(0, c.renderDPI())
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 := doc.ImageDPI(page, c.renderDPI())
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
}