pwa-launcher/pkg/image.go

140 lines
2.9 KiB
Go

package app
import (
"bytes"
"errors"
"fmt"
"image"
"image/png"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path/filepath"
)
// BestIcon selects the best PNG icon image.
func BestIcon(i Insights) (image.Image, error) {
// Ensure all icons are loaded.
for _, icon := range i.Icons {
if icon.Data == nil {
img, err := ParseWebPNG(icon.URL)
if err == nil {
icon.Data = img
}
}
}
// Get the biggest icon.
var biggest image.Image
var maxSize int
for _, icon := range i.Icons {
w := icon.Width()
if w > maxSize && icon.Data != nil {
biggest = icon.Data
maxSize = w
}
}
if biggest == nil {
return nil, errors.New("no suitable icon available")
}
return biggest, nil
}
// ParsePNG parses a PNG image.
func ParsePNG(r io.Reader) (image.Image, error) {
img, err := png.Decode(r)
return img, err
}
// ParseWebPNG parses a PNG image by HTTP URL.
func ParseWebPNG(url string) (image.Image, error) {
resp, err := http.Get(AddScheme(url))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ParsePNG(resp.Body)
}
// IcoToPNG converts a .ico file into PNG images.
func IcoToPNG(r io.Reader) ([]image.Image, error) {
var result = []image.Image{}
bin, _ := ioutil.ReadAll(r)
// Check if the image is already a PNG format.
if string(bin[1:4]) == "PNG" {
fmt.Printf("IcoToPNG: ico is already a png format!")
buf := bytes.NewBuffer(bin)
img, err := png.Decode(buf)
if err != nil {
return result, err
}
return []image.Image{img}, nil
}
// Ensure we have icotool available.
if !HasICOTool() {
return result, errors.New("icotool not available")
}
// Create a temp directory to extract this icon.
dir, err := ioutil.TempDir("", "pwa-launcher")
if err != nil {
return result, fmt.Errorf("TempDir: %s", err)
}
log.Printf("Temp dir to extract .ico file: %s", dir)
defer os.RemoveAll(dir)
// Write the .ico binary to the source file.
icoFile := filepath.Join(dir, "favicon.ico")
fh, err := os.Create(icoFile)
if err != nil {
return result, fmt.Errorf("Write %s: %s", icoFile, err)
}
fh.Write(bin)
fh.Close()
// Run the commands.
cmd := exec.Command(
"icotool", "-x", "-o", dir, icoFile,
)
stdout, err := cmd.CombinedOutput()
fmt.Println(stdout)
// Glom the PNG images.
files, _ := filepath.Glob(filepath.Join(dir, "*.png"))
for _, file := range files {
fh, err := os.Open(file)
if err != nil {
continue
}
if img, err := ParsePNG(fh); err == nil {
result = append(result, img)
}
fh.Close()
}
return result, nil
}
// HasICOTool checks if the icotool binary is available.
func HasICOTool() bool {
_, err := exec.LookPath("icotool")
if err != nil {
fmt.Printf(
"********\n" +
"WARNING: command `icotool` was not found on your system.\n" +
"Extracting PNG images from .ico files will not be supported.\n" +
"To remedy this, install icoutils, e.g. `sudo apt install icoutils`\n" +
"********\n",
)
}
return true
}