fix(font): install correctly on Windows

This commit is contained in:
Jan De Dobbeleer 2022-06-08 20:02:30 +02:00 committed by Jan De Dobbeleer
parent c301864021
commit 265790659c

View file

@ -1,34 +1,37 @@
// Derived from https://github.com/Crosse/font-install
// Copyright 2020 Seth Wright <seth@crosse.org>
package font
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"syscall"
"unsafe"
"golang.org/x/sys/windows/registry"
)
// FontsDir denotes the path to the user's fonts directory on Linux.
// Windows doesn't have the concept of a permanent, per-user collection
// of fonts, meaning that all fonts are stored in the system-level fonts
// directory, which is %WINDIR%\Fonts by default.
var FontsDir = path.Join(os.Getenv("WINDIR"), "Fonts")
// https://docs.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-addfontresourcea
var FontsDir = filepath.Join(os.Getenv("WINDIR"), "Fonts")
const (
WM_FONTCHANGE = 0x001D // nolint:revive
HWND_BROADCAST = 0xFFFF // nolint:revive
)
func install(font *Font) (err error) {
// To install a font on Windows:
// - Copy the file to the fonts directory
// - Create a registry entry for the font
fullPath := path.Join(FontsDir, font.FileName)
err = ioutil.WriteFile(fullPath, font.Data, 0644) //nolint:gosec
// - Add registry entry
// - Call AddFontResourceW to set the font
// - Notify other applications that the fonts have changed
fullPath := filepath.Join(FontsDir, font.FileName)
err = os.WriteFile(fullPath, font.Data, 0644)
if err != nil {
return err
return
}
// Second, write metadata about the font to the registry.
// Add registry entry
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`, registry.WRITE)
if err != nil {
// If this fails, remove the font file as well.
@ -40,12 +43,8 @@ func install(font *Font) (err error) {
}
defer k.Close()
// Apparently it's "ok" to mark an OpenType font as "TrueType",
// and since this tool only supports True- and OpenType fonts,
// this should be Okay(tm).
// Besides, Windows does it, so why can't I?
valueName := fmt.Sprintf("%v (TrueType)", font.FileName)
if err = k.SetStringValue(font.Name, valueName); err != nil {
name := fmt.Sprintf("%v (TrueType)", font.Name)
if err = k.SetStringValue(name, font.FileName); err != nil {
// If this fails, remove the font file as well.
if nexterr := os.Remove(fullPath); nexterr != nil {
return nexterr
@ -54,5 +53,26 @@ func install(font *Font) (err error) {
return err
}
gdi32 := syscall.NewLazyDLL("gdi32.dll")
proc := gdi32.NewProc("AddFontResourceW")
fontPtr, err := syscall.UTF16PtrFromString(fullPath)
if err != nil {
return
}
ret, _, _ := proc.Call(uintptr(unsafe.Pointer(fontPtr)))
if ret == 0 {
return errors.New("unable to add font resource")
}
// Notify other applications that the fonts have changed
user32 := syscall.NewLazyDLL("user32.dll")
procSendMessageW := user32.NewProc("SendMessageW")
_, _, e1 := syscall.SyscallN(procSendMessageW.Addr(), uintptr(HWND_BROADCAST), uintptr(WM_FONTCHANGE), 0, 0)
if e1 != 0 {
return errors.New("unable to broadcast font change")
}
return nil
}