package web
import (
"crypto/sha256"
"embed"
"encoding/hex"
"fmt"
"net/http"
"strings"
)
// The typeface every page is set in, served from this origin like
// everything else (the privacy page promises no other host is asked
// for anything). IBM Plex, OFL-1.1, Latin-1 subsets: about 120 KB for
// all six files, each cached for a year under a content-hashed name.
//
//go:embed assets/fonts/*.woff2
var fontFiles embed.FS
type webFont struct {
file string // under assets/fonts
family string
weight int
url string
body []byte
}
// webFonts is every face quarry serves. The stylesheet uses only these
// weights; a weight between them (a 550, say) is drawn by the browser
// from the nearest real face, so TestWeightsHaveAFace keeps the
// stylesheet to these.
var webFonts = loadFonts([]webFont{
{file: "IBMPlexSans-Regular.woff2", family: "IBM Plex Sans", weight: 400},
{file: "IBMPlexSans-Medium.woff2", family: "IBM Plex Sans", weight: 500},
{file: "IBMPlexSans-SemiBold.woff2", family: "IBM Plex Sans", weight: 600},
{file: "IBMPlexSans-Bold.woff2", family: "IBM Plex Sans", weight: 700},
{file: "IBMPlexMono-Regular.woff2", family: "IBM Plex Mono", weight: 400},
{file: "IBMPlexMono-Medium.woff2", family: "IBM Plex Mono", weight: 500},
})
func loadFonts(fs []webFont) []webFont {
for i := range fs {
b, err := fontFiles.ReadFile("assets/fonts/" + fs[i].file)
if err != nil {
panic("fonts: " + err.Error())
}
sum := sha256.Sum256(b)
name := strings.TrimSuffix(fs[i].file, ".woff2")
fs[i].body = b
fs[i].url = "/assets/fonts/" + name + "." + hex.EncodeToString(sum[:8]) + ".woff2"
}
return fs
}
// fontFaceCSS declares the faces. font-display: swap draws the text in
// the fallback at once and swaps the face in when it arrives, so a slow
// font never holds a page blank.
func fontFaceCSS() string {
var b strings.Builder
for _, f := range webFonts {
fmt.Fprintf(&b, "@font-face{font-family:%q;font-style:normal;font-weight:%d;font-display:swap;src:url(%s) format(\"woff2\")}\n",
f.family, f.weight, f.url)
}
return b.String()
}
// serveFonts mounts each face at its hashed URL.
func serveFonts(mux *http.ServeMux) {
for _, f := range webFonts {
body := f.body
mux.HandleFunc("GET "+f.url, func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "font/woff2")
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
w.Write(body)
})
}
}