Monorepo for Tangled
1package state
2
3import (
4 "bytes"
5 "fmt"
6 "html/template"
7 "image"
8 "image/color"
9 "net/http"
10
11 "github.com/srwiley/oksvg"
12 "github.com/srwiley/rasterx"
13 "golang.org/x/image/draw"
14 "tangled.org/core/appview/pages"
15 "tangled.org/core/ico"
16)
17
18func (s *State) FaviconSvg(w http.ResponseWriter, r *http.Request) {
19 w.Header().Set("Content-Type", "image/svg+xml")
20 w.Header().Set("Cache-Control", "public, max-age=31536000") // one year
21 w.Header().Set("ETag", `"favicon-svg-v1"`)
22
23 if match := r.Header.Get("If-None-Match"); match == `"favicon-svg-v1"` {
24 w.WriteHeader(http.StatusNotModified)
25 return
26 }
27
28 s.pages.Favicon(w)
29}
30
31func (s *State) FaviconIco(w http.ResponseWriter, r *http.Request) {
32 w.Header().Set("Content-Type", "image/x-icon")
33 w.Header().Set("Cache-Control", "public, max-age=31536000") // one year
34 w.Header().Set("ETag", `"favicon-ico-v1"`)
35
36 if match := r.Header.Get("If-None-Match"); match == `"favicon-ico-v1"` {
37 w.WriteHeader(http.StatusNotModified)
38 return
39 }
40
41 ico, err := dollyIco()
42 if err != nil {
43 s.logger.Error("failed to render ico", "err", err)
44 w.WriteHeader(http.StatusNotFound)
45 return
46 }
47
48 w.Write(ico)
49}
50
51func dollyIco() ([]byte, error) {
52 // first, get the bytes from the svg
53 tpl, err := template.New("dolly").
54 ParseFS(pages.Files, "templates/fragments/dolly/silhouette.html")
55 if err != nil {
56 return nil, err
57 }
58
59 var svgData bytes.Buffer
60 if err := tpl.ExecuteTemplate(&svgData, "fragments/dolly/silhouette", "#000000"); err != nil {
61 return nil, err
62 }
63
64 img, err := svgToImage(svgData.Bytes(), 48, 48)
65 if err != nil {
66 return nil, err
67 }
68
69 ico, err := ico.ImageToIco(img)
70 if err != nil {
71 return nil, err
72 }
73
74 return ico, nil
75}
76
77func svgToImage(svgData []byte, w, h int) (image.Image, error) {
78 icon, err := oksvg.ReadIconStream(bytes.NewReader(svgData))
79 if err != nil {
80 return nil, fmt.Errorf("error parsing SVG: %v", err)
81 }
82
83 icon.SetTarget(0, 0, float64(w), float64(h))
84 rgba := image.NewRGBA(image.Rect(0, 0, w, h))
85 draw.Draw(rgba, rgba.Bounds(), &image.Uniform{color.Transparent}, image.Point{}, draw.Src)
86 scanner := rasterx.NewScannerGV(w, h, rgba, rgba.Bounds())
87 raster := rasterx.NewDasher(w, h, scanner)
88 icon.Draw(raster, 1.0)
89
90 return rgba, nil
91}