// 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 ofdgo import ( "fmt" "path" "github.com/tdewolff/canvas" "github.com/tdewolff/font" ) // ResolveFont 按需加载字体资源,保留内嵌数据及外部字体的名称、样式和集合索引匹配 // 入参: r 渲染器, id 字体标识, exact 是否禁止无关回退 // 返回: ResolvedFont 字体与来源, error 字体不可用错误 func (CanvasBackend) ResolveFont(r *Renderer, id string, exact bool) (ResolvedFont, error) { definition := r.Reader.fontCache[id] if definition == nil && !r.Reader.fontResourcesRead && r.Reader.OFD != nil { if _, err := r.Reader.Fonts(); err != nil { return ResolvedFont{}, err } definition = r.Reader.fontCache[id] } if definition == nil { if exact { return ResolvedFont{}, fmt.Errorf("font %q not found", id) } return r.canvasFallbackFont(), nil } if definition.FontFile != "" { data, err := r.Reader.FontData(id) if err != nil { return ResolvedFont{}, err } face, err := r.Reader.embeddedFontFace(*definition) if err != nil { return ResolvedFont{}, err } return ResolvedFont{Data: data, Source: path.Base(definition.FontFile), Face: face, Exact: true}, nil } style := canvasFontStyle(definition) if source, family := r.fontSourceMatch(id, definition, exact); family != nil { sfnt := family.Face(12, style).Font.SFNT face := fontFaceInfo(sfnt.Tables["name"], source.face) r.fontSourceUsed[id] = source return ResolvedFont{Data: fontSFNTData(sfnt), Source: source.name, Face: &face, Exact: source.exact}, nil } if !exact { return r.canvasFallbackFont(), nil } return ResolvedFont{}, fmt.Errorf("font %q is unavailable", definition.FontName) } // canvasFallbackFont 按需加载默认阅读字体,不用于精确编辑匹配 // 返回: ResolvedFont 默认字体,未安装时数据为空 func (r *Renderer) canvasFallbackFont() ResolvedFont { if r.fontFamily == nil { r.initCanvasFonts() } if r.defaultFontLoaded { return ResolvedFont{Data: fontSFNTData(r.fontFamily.Face(12, canvas.FontRegular).Font.SFNT)} } return ResolvedFont{} } // OpenFont 解析独立SFNT数据,度量与原有编辑排版保持一致 // 入参: data 字体数据 // 返回: FontMetrics 字体度量, error 解析错误 func (CanvasBackend) OpenFont(data []byte) (FontMetrics, error) { sfnt, err := font.ParseSFNT(data, 0) if err != nil { return nil, err } return canvasFontMetrics{sfnt}, nil } // canvasFontMetrics 保留默认字体度量,写出时不更新原始时间 type canvasFontMetrics struct { *font.SFNT } // Write 返回字体数据,不修改字体元信息 // 返回: []byte 独立SFNT数据 func (f canvasFontMetrics) Write() []byte { return fontSFNTData(f.SFNT) } // canvasFontState 保存默认适配器的字体与字形缓存,不向后端接口暴露绘图库类型 type canvasFontState struct { fontFamily *canvas.FontFamily defaultFontLoaded bool fontMap map[string]*canvas.FontFamily fontGIDMap map[string]map[uint16]rune fontCIDMap map[string]map[uint16]rune fontCache map[fontCacheKey]*canvas.FontFamily svgFontCache map[*canvas.Font]SVGFont fontSourceCache map[string][]fontSource fontSourceUsed map[string]fontSource fontDirCandidates map[string][]fontFileCandidate fontFSCandidates map[int][]fontFileCandidate textGlyphPathCache map[textGlyphPathCacheKey]textGlyphPathCacheValue } // resetFontCache 重置字体匹配、加载和字形缓存 func (r *Renderer) resetFontCache() { r.resolvedFonts = make(map[resolvedFontKey]resolvedFontResult) r.fontMap = make(map[string]*canvas.FontFamily) r.fontGIDMap = make(map[string]map[uint16]rune) r.fontCIDMap = make(map[string]map[uint16]rune) r.fontCache = make(map[fontCacheKey]*canvas.FontFamily) r.svgFontCache = make(map[*canvas.Font]SVGFont) r.fontSourceCache = make(map[string][]fontSource) r.fontSourceUsed = make(map[string]fontSource) r.fontDirCandidates = make(map[string][]fontFileCandidate) r.fontFSCandidates = make(map[int][]fontFileCandidate) r.textGlyphPathCache = make(map[textGlyphPathCacheKey]textGlyphPathCacheValue) } // initCanvasFonts 初始化Canvas默认字体 func (r *Renderer) initCanvasFonts() { r.fontFamily = canvas.NewFontFamily("default") r.defaultFontLoaded = r.loadDefaultFonts() }