font Serve Vazirmatn WOFF2 with explicit Content-Type headers
Test and publish / verify (push) Successful in 3m16s
Test and publish / verify (push) Successful in 3m16s
Switch Vazirmatn font-face from TTF-only to WOFF2 to halve the font payload (~50 KB vs ~105 KB per weight). Add a staticHandler middleware that injects font/ttf and font/woff2 Content-Type headers so browsers don't reject them as application/octet-stream.
This commit is contained in:
+19
-1
@@ -267,8 +267,26 @@ func (s *Server) ListenAndServe() error {
|
|||||||
|
|
||||||
func (s *Server) Close() error { return s.store.Close() }
|
func (s *Server) Close() error { return s.store.Close() }
|
||||||
|
|
||||||
|
// staticHandler wraps a file server so font files always get the correct
|
||||||
|
// Content-Type — Go's http.DetectContentType can return application/octet-stream
|
||||||
|
// for .ttf files on older Go versions.
|
||||||
|
func staticHandler(fs http.FileSystem) http.Handler {
|
||||||
|
h := http.FileServer(fs)
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
path := strings.TrimPrefix(r.URL.Path, "/static/")
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(path, ".ttf"):
|
||||||
|
w.Header().Set("Content-Type", "font/ttf")
|
||||||
|
case strings.HasSuffix(path, ".woff"), strings.HasSuffix(path, ".woff2"):
|
||||||
|
w.Header().Set("Content-Type", "font/woff2")
|
||||||
|
}
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) routes(mux *http.ServeMux) {
|
func (s *Server) routes(mux *http.ServeMux) {
|
||||||
mux.Handle("GET /static/", http.FileServer(http.FS(assets)))
|
fs := http.FS(assets)
|
||||||
|
mux.Handle("GET /static/", staticHandler(fs))
|
||||||
mux.Handle("GET /uploads/avatars/", http.StripPrefix("/uploads/avatars/", http.FileServer(http.Dir(filepath.Join(filepath.Dir(s.cfg.DatabasePath), "avatars")))))
|
mux.Handle("GET /uploads/avatars/", http.StripPrefix("/uploads/avatars/", http.FileServer(http.Dir(filepath.Join(filepath.Dir(s.cfg.DatabasePath), "avatars")))))
|
||||||
mux.HandleFunc("GET /healthz", s.health)
|
mux.HandleFunc("GET /healthz", s.health)
|
||||||
mux.HandleFunc("GET /login", s.loginPage)
|
mux.HandleFunc("GET /login", s.loginPage)
|
||||||
|
|||||||
@@ -934,3 +934,36 @@ func extractCSRF(t *testing.T, body string) string {
|
|||||||
}
|
}
|
||||||
return match[1]
|
return match[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStaticHandlerSetsFontContentTypes(t *testing.T) {
|
||||||
|
s, err := New(Config{
|
||||||
|
Addr: ":0",
|
||||||
|
BaseURL: "http://example.test",
|
||||||
|
DatabasePath: filepath.Join(t.TempDir(), "font.db"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
path string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"/static/vendor/fonts/webfonts/Vazirmatn-400.ttf", "font/ttf"},
|
||||||
|
{"/static/vendor/fonts/webfonts/Vazirmatn-Bold.woff2", "font/woff2"},
|
||||||
|
{"/static/vendor/fontawesome.min.css", "text/css; charset=utf-8"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
req := httptest.NewRequest(http.MethodGet, tt.path, nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
s.http.Handler.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Errorf("%s: status %d", tt.path, rec.Code)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ct := rec.Header().Get("Content-Type"); ct != tt.want {
|
||||||
|
t.Errorf("%s: Content-Type = %q, want %q", tt.path, ct, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-9
@@ -1,10 +1,13 @@
|
|||||||
/* Vazirmatn faces downloaded from Google Fonts and bundled for offline use. */
|
/* Vazirmatn faces downloaded from Google Fonts and bundled for offline use. */
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-100.ttf') format('truetype'); font-weight: 100; font-style: normal; font-display: swap; }
|
/* Only WOFF2 referenced — all named-weight WOFF2 files exist;
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-200.ttf') format('truetype'); font-weight: 200; font-style: normal; font-display: swap; }
|
numeric-weight TTF fallbacks exist but the named weights
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-300.ttf') format('truetype'); font-weight: 300; font-style: normal; font-display: swap; }
|
(Regular/Bold/etc.) don't have TTF versions on disk. */
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-400.ttf') format('truetype'); font-weight: 400; font-style: normal; font-display: swap; }
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-Thin.woff2') format('woff2'); font-weight: 100; font-style: normal; font-display: swap; }
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-500.ttf') format('truetype'); font-weight: 500; font-style: normal; font-display: swap; }
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-ExtraLight.woff2') format('woff2'); font-weight: 200; font-style: normal; font-display: swap; }
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-600.ttf') format('truetype'); font-weight: 600; font-style: normal; font-display: swap; }
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-Light.woff2') format('woff2'); font-weight: 300; font-style: normal; font-display: swap; }
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-700.ttf') format('truetype'); font-weight: 700; font-style: normal; font-display: swap; }
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-800.ttf') format('truetype'); font-weight: 800; font-style: normal; font-display: swap; }
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
|
||||||
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-900.ttf') format('truetype'); font-weight: 900; font-style: normal; font-display: swap; }
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-SemiBold.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; }
|
||||||
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-Bold.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; }
|
||||||
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-ExtraBold.woff2') format('woff2'); font-weight: 800; font-style: normal; font-display: swap; }
|
||||||
|
@font-face { font-family: Vazirmatn; src: url('fonts/webfonts/Vazirmatn-Black.woff2') format('woff2'); font-weight: 900; font-style: normal; font-display: swap; }
|
||||||
|
|||||||
Reference in New Issue
Block a user