diff --git a/internal/base/font/file.go b/internal/base/font/file.go index a142d4d..13b4864 100644 --- a/internal/base/font/file.go +++ b/internal/base/font/file.go @@ -36,6 +36,13 @@ type Face struct { // 入参: data 名称表数据, index 零起始字体索引 // 返回: Face 字体信息 func FaceInfo(data []byte, index int) Face { + return faceInfoLanguage(data, index, 0xffff, 0xffff) +} + +// faceInfoLanguage 优先读取指定平台和语言的显示名称,保留标识及全部别名 +// 入参: data 名称表数据, index 字体索引, platform 平台标识, language 语言标识 +// 返回: Face 字体信息 +func faceInfoLanguage(data []byte, index int, platform, language uint16) Face { records, values := fontNameValues(data) name := func(ids ...uint16) string { for _, id := range ids { @@ -49,7 +56,19 @@ func FaceInfo(data []byte, index int) Face { } return "" } - return Face{Index: index, FullName: name(4, 6, 16, 1), Family: name(16, 1), Style: name(17, 2), PostScriptName: name(6), Names: fontNamesFromValues(records, values)} + localized := func(ids ...uint16) string { + for _, id := range ids { + for _, record := range records { + if record.Platform == platform && record.Language == language && record.Name == id { + if value := strings.TrimSpace(values[[4]uint16{record.Platform, record.Encoding, record.Language, id}]); value != "" { + return value + } + } + } + } + return name(ids...) + } + return Face{Index: index, FullName: localized(4, 6, 16, 1), Family: localized(16, 1), Style: localized(17, 2), PostScriptName: name(6), Names: fontNamesFromValues(records, values)} } // FileCount 校验字体或集合头并读取字体数量 @@ -78,6 +97,13 @@ func FileCount(data []byte) (int, error) { // 入参: data OpenType数据 // 返回: []Face 字体列表, error 格式或名称表错误 func ReadFaces(data []byte) ([]Face, error) { + return ReadLocalizedFaces(data, 0xffff, 0xffff) +} + +// ReadLocalizedFaces 读取指定平台和语言的字体名称,缺失时沿用名称表顺序 +// 入参: data OpenType数据, platform 名称表平台标识, language 名称表语言标识 +// 返回: []Face 字体列表, error 格式或名称表错误 +func ReadLocalizedFaces(data []byte, platform, language uint16) ([]Face, error) { count, err := FileCount(data) if err != nil { return nil, err @@ -88,7 +114,7 @@ func ReadFaces(data []byte) ([]Face, error) { if err != nil { return nil, err } - face := FaceInfo(tables["name"], index) + face := faceInfoLanguage(tables["name"], index, platform, language) if face.FullName == "" { return nil, fmt.Errorf("font %d has no usable name", index) } diff --git a/internal/webview/webview.go b/internal/webview/webview.go index 4a4e61e..4c5dc13 100644 --- a/internal/webview/webview.go +++ b/internal/webview/webview.go @@ -687,12 +687,13 @@ func (h *Host) cleanupOutput(ctx context.Context, target webui.NativeOutputTarge // readFonts 建立系统字体目录并登记按需读取资源 func (h *Host) readFonts() { + platform, language := fontNameLanguage() for _, name := range ofdgo.FontSystemFiles() { data, err := os.ReadFile(name) if err != nil { continue } - faces, err := (ofdgo.FontFile{Name: filepath.Base(name), Data: data}).Faces() + faces, err := (ofdgo.FontFile{Name: filepath.Base(name), Data: data}).LocalizedFaces(platform, language) if err != nil { continue } diff --git a/internal/webview/webview_memory_other.go b/internal/webview/webview_memory_other.go deleted file mode 100644 index eb4ba83..0000000 --- a/internal/webview/webview_memory_other.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2025-2026 肖其顿 (XIAO QI DUN) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//go:build !windows - -package webview - -// conversionMemory 未提供系统内存探测时仅按并行数调度 -var conversionMemory func() int64 diff --git a/internal/webview/webview_memory_windows.go b/internal/webview/webview_memory_windows.go deleted file mode 100644 index 9da11bd..0000000 --- a/internal/webview/webview_memory_windows.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2025-2026 肖其顿 (XIAO QI DUN) -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webview - -import ( - "syscall" - "unsafe" -) - -// memoryStatusProc 查询系统物理内存和提交额度 -var memoryStatusProc = syscall.NewLazyDLL("kernel32.dll").NewProc("GlobalMemoryStatusEx") - -// conversionMemory 预留系统内存后返回可用于新增转换的额度 -// 返回: int64 可用字节数,查询失败时暂停扩容 -func conversionMemory() int64 { - status := struct { - length uint32 - load uint32 - total uint64 - available uint64 - totalPage uint64 - availablePage uint64 - totalVirtual uint64 - availableVirtual uint64 - extended uint64 - }{} - status.length = uint32(unsafe.Sizeof(status)) - ok, _, _ := memoryStatusProc.Call(uintptr(unsafe.Pointer(&status))) - if ok == 0 { - return 0 - } - return max(0, int64(min(status.available, status.availablePage))-int64(status.total/8)) -} diff --git a/internal/webview/webview_files_other.go b/internal/webview/webview_other.go similarity index 73% rename from internal/webview/webview_files_other.go rename to internal/webview/webview_other.go index 573f210..80ea1f2 100644 --- a/internal/webview/webview_files_other.go +++ b/internal/webview/webview_other.go @@ -18,6 +18,9 @@ package webview import "os" +// conversionMemory 未提供系统内存探测时仅按并行数调度 +var conversionMemory func() int64 + // openReadFile 打开独立读取句柄 func openReadFile(name string) (*os.File, error) { return os.Open(name) @@ -27,3 +30,9 @@ func openReadFile(name string) (*os.File, error) { func replaceFile(source, target string) error { return os.Rename(source, target) } + +// fontNameLanguage 未指定系统显示语言时沿用字体名称表顺序 +// 返回: uint16 未指定平台标识, uint16 未指定语言标识 +func fontNameLanguage() (uint16, uint16) { + return 0xffff, 0xffff +} diff --git a/internal/webview/webview_files_windows.go b/internal/webview/webview_windows.go similarity index 67% rename from internal/webview/webview_files_windows.go rename to internal/webview/webview_windows.go index b8a5507..613d554 100644 --- a/internal/webview/webview_files_windows.go +++ b/internal/webview/webview_windows.go @@ -25,6 +25,9 @@ import ( // replaceFileProc 保留目标文件属性并替换文件内容 var replaceFileProc = syscall.NewLazyDLL("kernel32.dll").NewProc("ReplaceFileW") +// memoryStatusProc 查询系统物理内存和提交额度 +var memoryStatusProc = syscall.NewLazyDLL("kernel32.dll").NewProc("GlobalMemoryStatusEx") + // openReadFile 允许原子替换路径,同时保持已有读取句柄有效 func openReadFile(name string) (*os.File, error) { path, err := nativeFilePath(name) @@ -73,3 +76,32 @@ func nativeFilePath(name string) (*uint16, error) { } return syscall.UTF16PtrFromString(path) } + +// fontNameLanguage 获取系统显示语言对应的OpenType名称表标识 +// 返回: uint16 名称表平台标识, uint16 系统显示语言标识 +func fontNameLanguage() (uint16, uint16) { + language, _, _ := syscall.NewLazyDLL("kernel32.dll").NewProc("GetUserDefaultUILanguage").Call() + return 3, uint16(language) +} + +// conversionMemory 预留系统内存后返回可用于新增转换的额度 +// 返回: int64 可用字节数,查询失败时暂停扩容 +func conversionMemory() int64 { + status := struct { + length uint32 + load uint32 + total uint64 + available uint64 + totalPage uint64 + availablePage uint64 + totalVirtual uint64 + availableVirtual uint64 + extended uint64 + }{} + status.length = uint32(unsafe.Sizeof(status)) + ok, _, _ := memoryStatusProc.Call(uintptr(unsafe.Pointer(&status))) + if ok == 0 { + return 0 + } + return max(0, int64(min(status.available, status.availablePage))-int64(status.total/8)) +} diff --git a/ofdgo_font.go b/ofdgo_font.go index 5de6c6a..a2e69d5 100644 --- a/ofdgo_font.go +++ b/ofdgo_font.go @@ -44,6 +44,18 @@ func (f FontFile) Faces() ([]FontFace, error) { return basefont.ReadFaces(data) } +// LocalizedFaces 读取指定语言的显示名称,保留PostScript名称、全部别名和集合索引 +// 未找到对应名称时沿用Faces的名称选择规则,不修改字体数据 +// 入参: platform OpenType名称表平台标识, language 该平台的语言标识 +// 返回: []FontFace 字体列表, error 格式或名称表错误 +func (f FontFile) LocalizedFaces(platform, language uint16) ([]FontFace, error) { + data, err := decodeFontContainer(f.Data) + if err != nil { + return nil, err + } + return basefont.ReadLocalizedFaces(data, platform, language) +} + // Face 提取指定字体,返回独立的OpenType数据,可用于预览或AddFont,且不修改原文件 // 入参: index 零起始字体索引,非集合文件只能为0 // 返回: []byte 独立字体数据, error 格式或索引错误