140 lines
2.9 KiB
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
|
|
}
|