feat(font): allow installing from a specific zip file folder
Some checks are pending
Code QL / code-ql (push) Waiting to run
Azure Static Web Apps CI/CD / Build and Deploy (push) Waiting to run
Release / changelog (push) Waiting to run
Release / artifacts (push) Blocked by required conditions
Release / msi (arm64) (push) Blocked by required conditions
Release / msi (x64) (push) Blocked by required conditions
Release / msi (x86) (push) Blocked by required conditions
Release / release (push) Blocked by required conditions

This commit is contained in:
Jan De Dobbeleer 2024-12-17 12:03:16 +01:00 committed by Jan De Dobbeleer
parent b4b2fd02d5
commit a7ad857a2d
4 changed files with 73 additions and 54 deletions

View file

@ -11,7 +11,7 @@ import (
)
var (
ttf bool
zipFolder string
fontCmd = &cobra.Command{
Use: "font [install|configure]",
@ -47,7 +47,7 @@ This command is used to install fonts and configure the font in your terminal.
terminal.Init(env.Shell())
font.Run(fontName, env.Cache(), env.Root(), ttf)
font.Run(fontName, env.Cache(), env.Root(), zipFolder)
return
case "configure":
@ -60,6 +60,6 @@ This command is used to install fonts and configure the font in your terminal.
)
func init() {
fontCmd.Flags().BoolVar(&ttf, "ttf", false, "fetch the TTF version of the font")
fontCmd.Flags().StringVar(&zipFolder, "zip-folder", "", "the folder inside the zip file to install fonts from")
RootCmd.AddCommand(fontCmd)
}

View file

@ -72,14 +72,14 @@ const (
)
type main struct {
err error
list *list.Model
font string
families []string
spinner spinner.Model
state state
system bool
ttf bool
err error
list *list.Model
font string
zipFolder string
families []string
spinner spinner.Model
state state
system bool
}
func (m *main) buildFontList(nerdFonts []*Asset) {
@ -121,18 +121,18 @@ func downloadFontZip(location string) {
program.Send(zipMsg(zipFile))
}
func installLocalFontZIP(zipFile string, user, ttf bool) {
data, err := os.ReadFile(zipFile)
func installLocalFontZIP(m *main) {
data, err := os.ReadFile(m.font)
if err != nil {
program.Send(errMsg(err))
return
}
installFontZIP(data, user, ttf)
installFontZIP(data, m)
}
func installFontZIP(zipFile []byte, user, ttf bool) {
families, err := InstallZIP(zipFile, user, ttf)
func installFontZIP(zipFile []byte, m *main) {
families, err := InstallZIP(zipFile, m)
if err != nil {
program.Send(errMsg(err))
return
@ -148,21 +148,30 @@ func (m *main) Init() tea.Cmd {
if len(m.font) != 0 && !isLocalZipFile() {
m.state = downloadFont
if !strings.HasPrefix(m.font, "https") {
m.font = fmt.Sprintf("https://github.com/ryanoasis/nerd-fonts/releases/latest/download/%s.zip", m.font)
if strings.HasPrefix(m.font, "CascadiaCode-") {
version := strings.TrimPrefix(m.font, "CascadiaCode-")
m.font = fmt.Sprintf("https://github.com/microsoft/cascadia-code/releases/download/v%s/%s.zip", version, m.font)
} else {
m.font = fmt.Sprintf("https://github.com/ryanoasis/nerd-fonts/releases/latest/download/%s.zip", m.font)
}
}
defer func() {
go downloadFontZip(m.font)
}()
m.spinner.Spinner = spinner.Globe
return m.spinner.Tick
}
defer func() {
if isLocalZipFile() {
go installLocalFontZIP(m.font, m.system, m.ttf)
go installLocalFontZIP(m)
return
}
go getFontsList()
}()
@ -171,6 +180,7 @@ func (m *main) Init() tea.Cmd {
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("170"))
m.spinner = s
m.state = getFonts
if isLocalZipFile() {
m.state = unzipFont
}
@ -240,7 +250,7 @@ func (m *main) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case zipMsg:
m.state = installFont
defer func() {
go installFontZIP(msg, m.system, m.ttf)
go installFontZIP(msg, m)
}()
m.spinner.Spinner = spinner.Dot
return m, m.spinner.Tick
@ -288,6 +298,10 @@ func (m *main) View() string {
case quit:
return textStyle.Render(fmt.Sprintf("No need to install a new font? That's cool.%s", terminal.StopProgress()))
case done:
if len(m.families) == 0 {
return textStyle.Render(fmt.Sprintf("No matching font families were installed. Try setting --zip-folder to the correct folder when using Cascadia Code or a custom font zip file %s", terminal.StopProgress())) //nolint: lll
}
var builder strings.Builder
builder.WriteString(fmt.Sprintf("Successfully installed %s 🚀\n\n%s", m.font, terminal.StopProgress()))
@ -307,11 +321,11 @@ func (m *main) View() string {
return ""
}
func Run(font string, ch cache_.Cache, root, ttf bool) {
func Run(font string, ch cache_.Cache, root bool, zipFolder string) {
main := &main{
font: font,
system: root,
ttf: ttf,
font: font,
system: root,
zipFolder: zipFolder,
}
cache = ch

View file

@ -25,13 +25,7 @@ func contains[S ~[]E, E comparable](s S, e E) bool {
return false
}
func InstallZIP(data []byte, user, ttf bool) ([]string, error) {
// prefer OTF over TTF; otherwise prefer the first font we find
extension := ".otf"
if ttf {
extension = ".ttf"
}
func InstallZIP(data []byte, m *main) ([]string, error) {
var families []string
bytesReader := bytes.NewReader(data)
@ -42,46 +36,56 @@ func InstallZIP(data []byte, user, ttf bool) ([]string, error) {
fonts := make(map[string]*Font)
for _, zf := range zipReader.File {
root := len(m.zipFolder) == 0
for _, file := range zipReader.File {
// prevent zipslip attacks
// https://security.snyk.io/research/zip-slip-vulnerability
if strings.Contains(zf.Name, "..") {
// and only process files which are in the specified folder
if strings.Contains(file.Name, "..") || !strings.HasPrefix(file.Name, m.zipFolder) {
continue
}
rc, err := zf.Open()
if err != nil {
return families, err
fontFileName := path.Base(file.Name)
// do not install fonts that are not in the root folder when specified as such
if root && fontFileName != file.Name {
continue
}
defer rc.Close()
data, err := io.ReadAll(rc)
if err != nil {
return families, err
}
fontData, err := newFont(path.Base(zf.Name), data)
fontReader, err := file.Open()
if err != nil {
continue
}
if _, found := fonts[fontData.Name]; !found {
fonts[fontData.Name] = fontData
defer fontReader.Close()
fontBytes, err := io.ReadAll(fontReader)
if err != nil {
continue
}
// respect the user's preference for TTF or OTF
first := strings.ToLower(path.Ext(fonts[fontData.Name].FileName))
second := strings.ToLower(path.Ext(fontData.FileName))
if first != second && second == extension {
fonts[fontData.Name] = fontData
font, err := newFont(fontFileName, fontBytes)
if err != nil {
continue
}
if _, found := fonts[font.Name]; !found {
fonts[font.Name] = font
continue
}
// prefer .ttf files over other file types when we have a duplicate
first := strings.ToLower(path.Ext(fonts[font.Name].FileName))
second := strings.ToLower(path.Ext(font.FileName))
if first != second && second == ".ttf" {
fonts[font.Name] = font
}
}
for _, font := range fonts {
if err = install(font, user); err != nil {
return families, err
if err = install(font, m.system); err != nil {
continue
}
if found := contains(families, font.Family); !found {

View file

@ -34,6 +34,7 @@ Oh My Posh has a CLI to help you select and install a [Nerd Font][nerdfonts]:
:::info
When running as root/administrator, the fonts will be installed system-wide.
When running as a regular user, the fonts will be installed in the user's font directory.
By default, Oh My Posh installs the `.ttf` version of the font in case multiple versions are available.
:::
```bash
@ -46,10 +47,10 @@ This will present a list of Nerd Font libraries, from which you can select `Mes
oh-my-posh font install meslo
```
By default, Oh My Posh installs the `.otf` version of the font. If you prefer the `.ttf` version, you can specify it with the `--ttf` flag:
If you have a font that has specific flavors of a font inside sub folders, you can specify the sub folder name:
```bash
oh-my-posh font install meslo --ttf
oh-my-posh font install --zip-folder ttf/static CascadiaCode-2407.24
```
</TabItem>