Files
ofdgo/ofdgo_renderer.go
T
2026-10-05 12:49:51 +08:00

147 lines
5.1 KiB
Go

// 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 (
"context"
"image"
"io/fs"
)
// Renderer 渲染器实现
// 通过NewRenderer创建,实例及共享的Reader需串行使用
// 外部字体缺失且无可用回退时不绘制对应文字,其余内容继续;字体状态可通过FontInfos查询
// OnPageText可选,接收页面绘制时同步提取的文字,不包含图案和签名外观
// OnExportProgress可选,同步回报文档导出的已处理页数及总页数,返回错误则停止,页数完成不代表写入成功
// TransparentBackground关闭页面白底,供嵌套图案和印章保持透明背景
// Compression仅控制导出编码,OutputContext可取消输出压缩,nil使用后台上下文
type Renderer struct {
Reader *Reader
DPI float64
Compression CompressionOptions
OutputContext context.Context
RenderAnnotations bool
OnPageText func(*PageContent, *PageText)
OnExportProgress func(completed, total int) error
DrawParams map[string]*DrawParam
CompositeGraphicUnits map[string]*CompositeGraphicUnit
templatePageCache map[string]*PageContent
imageCache renderCache[string, image.Image]
fontDirs []string
fontFS []fs.FS
decodeImages bool
pageText *PageText
textOnly bool
backends RenderBackends
backendStates map[any]any
sharedBackendStates map[any]any
resolvedFonts map[resolvedFontKey]resolvedFontResult
preparedFonts map[string]*PreparedFont
fontDigests *renderCache[fontDataKey, [32]byte]
fontPreparations *renderCache[[32]byte, PreparedFont]
fontSourcesCache *fontSourceCache
fontMetrics *renderCache[[32]byte, FontMetrics]
glyphOutlines renderCache[glyphOutlineKey, GeometryPath]
renderError error
patternReferences map[*Pattern]bool
patternPage *Matrix
TransparentBackground bool
}
// RendererOption 渲染器配置选项
type RendererOption func(*Renderer)
// ClearCache 释放派生字体、图片、模板与后端缓存,不改变文档和渲染配置
func (r *Renderer) ClearCache() {
r.backendStates = make(map[any]any)
r.imageCache = renderCache[string, image.Image]{limit: imageCacheLimit}
r.templatePageCache = make(map[string]*PageContent)
r.resetFontCache()
}
// SetFontFS 替换外部字体文件系统并重置字体缓存
// 入参: fsys 字体文件系统
func (r *Renderer) SetFontFS(fsys ...fs.FS) {
r.fontFS = append([]fs.FS(nil), fsys...)
r.resetFontCache()
}
// FontSources 返回外部字体配置副本,供自定义编译器和导出后端读取
// 内嵌字体通过Reader.FontData读取,系统字体由各实现自行匹配
// 返回: []string 字体目录, []fs.FS 字体文件系统
func (r *Renderer) FontSources() ([]string, []fs.FS) {
return append([]string(nil), r.fontDirs...), append([]fs.FS(nil), r.fontFS...)
}
// childRenderer 继承配置并复用字体文件索引与数据,文档匹配结果保持独立
// 入参: reader 子阅读器
// 返回: *Renderer 子渲染器
func (r *Renderer) childRenderer(reader *Reader) *Renderer {
opts := []RendererOption{
WithDPI(r.DPI),
WithAnnotations(r.RenderAnnotations),
WithRenderBackends(r.backends),
}
if len(r.fontDirs) > 0 {
opts = append(opts, WithFontDirs(r.fontDirs...))
}
if len(r.fontFS) > 0 {
opts = append(opts, WithFontFS(r.fontFS...))
}
renderer := NewRenderer(reader, opts...)
renderer.sharedBackendStates = r.sharedBackendStates
renderer.fontPreparations = r.fontPreparations
renderer.fontDigests = r.fontDigests
renderer.fontMetrics = r.fontMetrics
renderer.fontSourcesCache = r.fontSourcesCache.child()
renderer.decodeImages = r.decodeImages
renderer.TransparentBackground = r.TransparentBackground
return renderer
}
// loadTemplate 加载模板页面并复用解析结果
// 入参: templateID 模板ID
// 返回: *PageContent 模板页面
func (r *Renderer) loadTemplate(templateID string) *PageContent {
if page := r.templatePageCache[templateID]; page != nil {
return page
}
for _, tpl := range r.Reader.doc.CommonData.TemplatePage {
if tpl.ID != templateID {
continue
}
page, err := r.Reader.PageContent(Page{BaseLoc: tpl.BaseLoc})
if err != nil {
return nil
}
r.templatePageCache[templateID] = page
return page
}
return nil
}
// layerOrder 获取图层类型的绘制顺序
// 入参: kind 图层类型
// 返回: int 绘制顺序
func layerOrder(kind string) int {
switch kind {
case "Background":
return 0
case "Foreground":
return 2
}
return 1
}