mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
17a9a03361
commit
c4b81155a9
19 files changed
+410
-142
No files matched your search
@@ -126,6 +126,14 @@ type annotationOptions struct {
|
|||||||
Area *ofdgo.Box
|
Area *ofdgo.Box
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// outputContext 定期让出浏览器线程,将取消请求传入库层长操作
|
||||||
|
type outputContext struct {
|
||||||
|
context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
checkpoint func() error
|
||||||
|
checked time.Time
|
||||||
|
}
|
||||||
|
|
||||||
// Write 将数据块交给浏览器保存
|
// Write 将数据块交给浏览器保存
|
||||||
// 入参: data 导出数据
|
// 入参: data 导出数据
|
||||||
// 返回: int 写入长度, error 错误信息
|
// 返回: int 写入长度, error 错误信息
|
||||||
@@ -4626,14 +4634,6 @@ func compressionFromJS(value js.Value, options *ofdgo.CompressionOptions) error
|
|||||||
return options.Validate()
|
return options.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// outputContext 定期让出浏览器线程,将取消请求传入库层长操作
|
|
||||||
type outputContext struct {
|
|
||||||
context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
checkpoint func() error
|
|
||||||
checked time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// newOutputContext 创建单次串行导出的取消上下文
|
// newOutputContext 创建单次串行导出的取消上下文
|
||||||
// 入参: checkpoint 浏览器取消检查回调
|
// 入参: checkpoint 浏览器取消检查回调
|
||||||
// 返回: context.Context 导出上下文
|
// 返回: context.Context 导出上下文
|
||||||
|
|||||||
@@ -139,6 +139,23 @@ type actionSource struct {
|
|||||||
Graphic bool
|
Graphic bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// decodeOFDActions 读取标准动作列表,不将外部同名节点作为交互动作
|
||||||
|
// 入参: d XML解码器, start 列表起始节点, actions 动作接收列表
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func decodeOFDActions(d *xml.Decoder, start xml.StartElement, actions *[]Action) error {
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
if node.Name.Local != "Action" {
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
var action Action
|
||||||
|
if err := d.DecodeElement(&action, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*actions = append(*actions, action)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalXML 解析跳转目标
|
// UnmarshalXML 解析跳转目标
|
||||||
// 入参: d XML解码器, start 起始节点
|
// 入参: d XML解码器, start 起始节点
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
|
|||||||
+107
-13
@@ -16,6 +16,7 @@ package ofdgo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -202,6 +203,112 @@ type print struct {
|
|||||||
Copies *int `xml:"Copies,attr"`
|
Copies *int `xml:"Copies,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 按命名空间读取文档结构,扩展数据仍由原解码器保留原文
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (doc *Document) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
if start.Name.Local != "Document" {
|
||||||
|
return fmt.Errorf("expected Document root, got %s", start.Name.Local)
|
||||||
|
}
|
||||||
|
*doc = Document{XMLName: start.Name, Permissions: defaultPermissions()}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
var target any
|
||||||
|
switch node.Name.Local {
|
||||||
|
case "CommonData":
|
||||||
|
target = &doc.CommonData
|
||||||
|
case "Pages":
|
||||||
|
target = &doc.Pages
|
||||||
|
case "Outlines":
|
||||||
|
target = &doc.Outlines
|
||||||
|
case "Permissions":
|
||||||
|
target = &doc.Permissions
|
||||||
|
case "Actions":
|
||||||
|
return decodeOFDActions(d, node, &doc.Actions)
|
||||||
|
case "Bookmarks":
|
||||||
|
target = &doc.Bookmarks
|
||||||
|
case "Annotations":
|
||||||
|
target = &doc.Annotations
|
||||||
|
case "Attachments":
|
||||||
|
target = &doc.Attachments
|
||||||
|
case "CustomTags":
|
||||||
|
target = &doc.CustomTags
|
||||||
|
case "Extensions":
|
||||||
|
target = &doc.Extensions
|
||||||
|
default:
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
return d.DecodeElement(target, &node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取文档公共区域、模板与资源引用,忽略外部同名节点
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (common *CommonData) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
*common = CommonData{}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
var target any
|
||||||
|
switch node.Name.Local {
|
||||||
|
case "MaxUnitID":
|
||||||
|
target = &common.MaxUnitID
|
||||||
|
case "PageArea":
|
||||||
|
target = &common.PageArea
|
||||||
|
case "PublicRes":
|
||||||
|
target = &common.PublicRes
|
||||||
|
case "DocumentRes":
|
||||||
|
target = &common.DocumentRes
|
||||||
|
case "TemplatePage":
|
||||||
|
target = &common.TemplatePage
|
||||||
|
case "DefaultCS":
|
||||||
|
target = &common.DefaultCS
|
||||||
|
default:
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
return d.DecodeElement(target, &node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取页面区域,防止外部同名字段覆盖标准区域
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (area *PageArea) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
*area = PageArea{}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
var target *string
|
||||||
|
switch node.Name.Local {
|
||||||
|
case "PhysicalBox":
|
||||||
|
target = &area.PhysicalBox
|
||||||
|
case "ApplicationBox":
|
||||||
|
target = &area.ApplicationBox
|
||||||
|
case "ContentBox":
|
||||||
|
target = &area.ContentBox
|
||||||
|
case "BleedBox":
|
||||||
|
target = &area.BleedBox
|
||||||
|
default:
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
return d.DecodeElement(target, &node)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取标准页面引用,忽略外部扩展中的同名条目
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (pages *Pages) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
*pages = Pages{}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
if node.Name.Local != "Page" {
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
var page Page
|
||||||
|
if err := d.DecodeElement(&page, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pages.Page = append(pages.Page, page)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// PageIndex 按标识查找页面,优先匹配原文和标准十进制写法,等价编号有歧义时不匹配
|
// PageIndex 按标识查找页面,优先匹配原文和标准十进制写法,等价编号有歧义时不匹配
|
||||||
// 入参: id 页面标识
|
// 入参: id 页面标识
|
||||||
// 返回: int 从0开始的页面索引,未找到时为-1
|
// 返回: int 从0开始的页面索引,未找到时为-1
|
||||||
@@ -249,19 +356,6 @@ func (doc *Document) OutlineInfos() []OutlineInfo {
|
|||||||
return outlineInfos(doc.Outlines.OutlineElem, bookmarks, pages)
|
return outlineInfos(doc.Outlines.OutlineElem, bookmarks, pages)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML 解析文档并应用权限默认值
|
|
||||||
// 入参: d XML解码器, start 起始节点
|
|
||||||
// 返回: error 错误信息
|
|
||||||
func (doc *Document) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|
||||||
type document Document
|
|
||||||
value := document{Permissions: defaultPermissions()}
|
|
||||||
if err := d.DecodeElement(&value, &start); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*doc = Document(value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileName 获取另存文件名,优先使用带扩展名的显示名称,剔除目录及文件名非法字符
|
// FileName 获取另存文件名,优先使用带扩展名的显示名称,剔除目录及文件名非法字符
|
||||||
// 返回: string 文件名
|
// 返回: string 文件名
|
||||||
func (a Attachment) FileName() string {
|
func (a Attachment) FileName() string {
|
||||||
|
|||||||
@@ -28,9 +28,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FontMode 指定字体资源的保存方式,不改变文字位置或压缩选项
|
|
||||||
type FontMode uint8
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// FontKeep 保留原有嵌入方式
|
// FontKeep 保留原有嵌入方式
|
||||||
FontKeep FontMode = iota
|
FontKeep FontMode = iota
|
||||||
@@ -40,6 +37,9 @@ const (
|
|||||||
FontExternal
|
FontExternal
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FontMode 指定字体资源的保存方式,不改变文字位置或压缩选项
|
||||||
|
type FontMode uint8
|
||||||
|
|
||||||
// FontChange 返回单个字体的处理结果,Document为零基文档索引,Reason非空表示保持原样
|
// FontChange 返回单个字体的处理结果,Document为零基文档索引,Reason非空表示保持原样
|
||||||
type FontChange struct {
|
type FontChange struct {
|
||||||
Document int `json:"document"`
|
Document int `json:"document"`
|
||||||
|
|||||||
@@ -24,7 +24,13 @@ import (
|
|||||||
// 入参: name 节点名称
|
// 入参: name 节点名称
|
||||||
// 返回: bool 是否匹配
|
// 返回: bool 是否匹配
|
||||||
func (n *editorXML) matchesOFD(name string) bool {
|
func (n *editorXML) matchesOFD(name string) bool {
|
||||||
return n.name.Local == name && (classifyOFDNamespace(n.name.Space) != ofdXMLUnknown || n.parent != nil && n.name.Space == n.parent.name.Space)
|
if n.name.Local != name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n.parent == nil {
|
||||||
|
return classifyOFDNamespace(n.name.Space) != ofdXMLUnknown
|
||||||
|
}
|
||||||
|
return ofdXMLChild(n.parent.name, n.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// textXML 按所在节点的命名空间编码文本节点
|
// textXML 按所在节点的命名空间编码文本节点
|
||||||
|
|||||||
@@ -380,6 +380,9 @@ func (e *Editor) repairSourceObjectIDs(data []byte, root *editorXML) ([]byte, bo
|
|||||||
var visit func(*editorXML) error
|
var visit func(*editorXML) error
|
||||||
visit = func(parent *editorXML) error {
|
visit = func(parent *editorXML) error {
|
||||||
for _, node := range parent.children {
|
for _, node := range parent.children {
|
||||||
|
if !ofdXMLChild(parent.name, node.name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
id := node.attr("ID")
|
id := node.attr("ID")
|
||||||
if id != "" && seen[id] {
|
if id != "" && seen[id] {
|
||||||
switch node.name.Local {
|
switch node.name.Local {
|
||||||
@@ -401,7 +404,7 @@ func (e *Editor) repairSourceObjectIDs(data []byte, root *editorXML) ([]byte, bo
|
|||||||
}
|
}
|
||||||
if content := root.child("Content"); content != nil {
|
if content := root.child("Content"); content != nil {
|
||||||
for _, layer := range content.children {
|
for _, layer := range content.children {
|
||||||
if layer.name.Local == "Layer" {
|
if layer.matchesOFD("Layer") {
|
||||||
if err := visit(layer); err != nil {
|
if err := visit(layer); err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
@@ -508,7 +511,7 @@ func (e *Editor) loadSourcePage(index int) error {
|
|||||||
nodes := make(map[string]*editorXML)
|
nodes := make(map[string]*editorXML)
|
||||||
if content := root.child("Content"); content != nil {
|
if content := root.child("Content"); content != nil {
|
||||||
for _, layer := range content.children {
|
for _, layer := range content.children {
|
||||||
if layer.name.Local != "Layer" {
|
if !layer.matchesOFD("Layer") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := editorXMLObjects(layer, nodes); err != nil {
|
if err := editorXMLObjects(layer, nodes); err != nil {
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// pageReferenceNode 记录跳转引用扫描中的父节点及删除范围
|
||||||
|
type pageReferenceNode struct {
|
||||||
|
name string
|
||||||
|
start, patch int
|
||||||
|
children, removed int
|
||||||
|
standard, drop bool
|
||||||
|
}
|
||||||
|
|
||||||
// pruneDocumentReferences 在输出快照中清理已删除页面和附件的引用,不修改历史快照或其他文档
|
// pruneDocumentReferences 在输出快照中清理已删除页面和附件的引用,不修改历史快照或其他文档
|
||||||
// 沿当前文档、页面、模板、资源、注释和签名索引检查,不进入附件、私有扩展和签名原始凭据
|
// 沿当前文档、页面、模板、资源、注释和签名索引检查,不进入附件、私有扩展和签名原始凭据
|
||||||
// 版本包保留原签名及其页面引用,避免改写其他版本共用的签名数据
|
// 版本包保留原签名及其页面引用,避免改写其他版本共用的签名数据
|
||||||
@@ -185,14 +193,6 @@ func (e *Editor) pruneDocumentReferences(parts map[string][]byte, generated map[
|
|||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// pageReferenceNode 记录跳转引用扫描中的父节点及删除范围
|
|
||||||
type pageReferenceNode struct {
|
|
||||||
name string
|
|
||||||
start, patch int
|
|
||||||
children, removed int
|
|
||||||
standard, drop bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// pruneCreatedPageReferences 清理创作文档中被明确删除页面的直接跳转,不检查其他目标是否可达
|
// pruneCreatedPageReferences 清理创作文档中被明确删除页面的直接跳转,不检查其他目标是否可达
|
||||||
// 入参: ctx 取消上下文, data 创作XML, removed 已删除页面标识
|
// 入参: ctx 取消上下文, data 创作XML, removed 已删除页面标识
|
||||||
// 返回: []byte 更新后的XML, error 解析或取消错误
|
// 返回: []byte 更新后的XML, error 解析或取消错误
|
||||||
|
|||||||
+22
-22
@@ -33,6 +33,28 @@ type editorGeneratedReferences struct {
|
|||||||
staged *editorStagedPage
|
staged *editorStagedPage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editorVectorUsage 保存自产矢量资源的引用快照,内容和路径变化时失效
|
||||||
|
type editorVectorUsage struct {
|
||||||
|
name string
|
||||||
|
digest [32]byte
|
||||||
|
refs *editorResourceRefs
|
||||||
|
}
|
||||||
|
|
||||||
|
// editorReferenceScan 按XML事件收集资源和字体用字,未知内容保持完整扫描
|
||||||
|
type editorReferenceScan struct {
|
||||||
|
refs *editorResourceRefs
|
||||||
|
objects map[string]bool
|
||||||
|
name string
|
||||||
|
base string
|
||||||
|
stack []string
|
||||||
|
fonts []*editorFontUsage
|
||||||
|
text strings.Builder
|
||||||
|
resource bool
|
||||||
|
capture bool
|
||||||
|
safe bool
|
||||||
|
seen bool
|
||||||
|
}
|
||||||
|
|
||||||
// matches 判断条目仍为本次编码结果,空条目与暂存占位不混用
|
// matches 判断条目仍为本次编码结果,空条目与暂存占位不混用
|
||||||
// 入参: data 当前条目数据
|
// 入参: data 当前条目数据
|
||||||
// 返回: bool 是否可以复用本次引用
|
// 返回: bool 是否可以复用本次引用
|
||||||
@@ -50,13 +72,6 @@ func (r editorGeneratedReferences) stagedLeaf(data []byte) bool {
|
|||||||
return r.staged != nil && data == nil && r.refs != nil && len(r.refs.files) == 0
|
return r.staged != nil && data == nil && r.refs != nil && len(r.refs.files) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// editorVectorUsage 保存自产矢量资源的引用快照,内容和路径变化时失效
|
|
||||||
type editorVectorUsage struct {
|
|
||||||
name string
|
|
||||||
digest [32]byte
|
|
||||||
refs *editorResourceRefs
|
|
||||||
}
|
|
||||||
|
|
||||||
// matches 核对资源路径和完整内容摘要,不以切片地址判断内容
|
// matches 核对资源路径和完整内容摘要,不以切片地址判断内容
|
||||||
// 入参: name 当前资源路径, data 当前资源内容
|
// 入参: name 当前资源路径, data 当前资源内容
|
||||||
// 返回: bool 是否仍可使用编码时的引用
|
// 返回: bool 是否仍可使用编码时的引用
|
||||||
@@ -89,21 +104,6 @@ func (e *Editor) generatedVectorReferences(parts map[string][]byte, generated ma
|
|||||||
return generated
|
return generated
|
||||||
}
|
}
|
||||||
|
|
||||||
// editorReferenceScan 按XML事件收集资源和字体用字,未知内容保持完整扫描
|
|
||||||
type editorReferenceScan struct {
|
|
||||||
refs *editorResourceRefs
|
|
||||||
objects map[string]bool
|
|
||||||
name string
|
|
||||||
base string
|
|
||||||
stack []string
|
|
||||||
fonts []*editorFontUsage
|
|
||||||
text strings.Builder
|
|
||||||
resource bool
|
|
||||||
capture bool
|
|
||||||
safe bool
|
|
||||||
seen bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// vectorUsage 保留完整且限额内的自产矢量引用,超限时保存流程重新扫描
|
// vectorUsage 保留完整且限额内的自产矢量引用,超限时保存流程重新扫描
|
||||||
// 入参: data 本次编码内容
|
// 入参: data 本次编码内容
|
||||||
// 返回: *editorVectorUsage 引用快照,无法复用时为空
|
// 返回: *editorVectorUsage 引用快照,无法复用时为空
|
||||||
|
|||||||
+4
-1
@@ -273,11 +273,14 @@ func editorXMLTextValue(data []byte, node *editorXML, before, after string) ([]b
|
|||||||
return append(entry, data[patch.end:node.end]...), nil
|
return append(entry, data[patch.end:node.end]...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// editorXMLObjects 递归登记图层和页块内的对象位置,保留原容器层级
|
// editorXMLObjects 递归登记图层和页块内的OFD对象位置,不进入外部扩展
|
||||||
// 入参: container 图层或页块, nodes 标识与节点映射
|
// 入参: container 图层或页块, nodes 标识与节点映射
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
func editorXMLObjects(container *editorXML, nodes map[string]*editorXML) error {
|
func editorXMLObjects(container *editorXML, nodes map[string]*editorXML) error {
|
||||||
for _, node := range container.children {
|
for _, node := range container.children {
|
||||||
|
if !ofdXMLChild(container.name, node.name) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if id := node.attr("ID"); id != "" {
|
if id := node.attr("ID"); id != "" {
|
||||||
if nodes[id] != nil {
|
if nodes[id] != nil {
|
||||||
return fmt.Errorf("duplicate object ID %q", id)
|
return fmt.Errorf("duplicate object ID %q", id)
|
||||||
|
|||||||
+3
-3
@@ -220,11 +220,11 @@ func (p *fontUsagePage) UnmarshalXML(d *xml.Decoder, start xml.StartElement) err
|
|||||||
if start.Name.Local != "Page" {
|
if start.Name.Local != "Page" {
|
||||||
return fmt.Errorf("expected element type <Page> but have <%s>", start.Name.Local)
|
return fmt.Errorf("expected element type <Page> but have <%s>", start.Name.Local)
|
||||||
}
|
}
|
||||||
return decodeObjectContainer(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
if node.Name.Local != "Content" {
|
if node.Name.Local != "Content" {
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
return decodeObjectContainer(d, node, func(d *xml.Decoder, layer xml.StartElement) error {
|
return decodeOFDChildren(d, node, func(d *xml.Decoder, layer xml.StartElement) error {
|
||||||
if layer.Name.Local != "Layer" {
|
if layer.Name.Local != "Layer" {
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -434,7 +434,7 @@ func (r *Renderer) readPageFontUsage(page Page) (map[string]int, error) {
|
|||||||
// 入参: d XML解码器, start 容器起始节点, defaults 默认绘制参数
|
// 入参: d XML解码器, start 容器起始节点, defaults 默认绘制参数
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
func (p *fontUsagePage) decodeObjects(d *xml.Decoder, start xml.StartElement, defaults *DrawParam) error {
|
func (p *fontUsagePage) decodeObjects(d *xml.Decoder, start xml.StartElement, defaults *DrawParam) error {
|
||||||
return decodeObjectContainer(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
switch node.Name.Local {
|
switch node.Name.Local {
|
||||||
case "PageBlock":
|
case "PageBlock":
|
||||||
return p.decodeObjects(d, node, defaults)
|
return p.decodeObjects(d, node, defaults)
|
||||||
|
|||||||
+7
-7
@@ -23,6 +23,13 @@ import (
|
|||||||
"math/bits"
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// bmpColorMask 保存一个BMP颜色分量的位域位置和取值范围
|
||||||
|
type bmpColorMask struct {
|
||||||
|
mask uint32
|
||||||
|
shift uint
|
||||||
|
max uint32
|
||||||
|
}
|
||||||
|
|
||||||
// decodeImageConfig 读取图片尺寸,BMP和TIFF只读取布局信息,不解码像素
|
// decodeImageConfig 读取图片尺寸,BMP和TIFF只读取布局信息,不解码像素
|
||||||
// 入参: input 图片输入流
|
// 入参: input 图片输入流
|
||||||
// 返回: image.Config 图片配置, string 图片格式, error 头部错误
|
// 返回: image.Config 图片配置, string 图片格式, error 头部错误
|
||||||
@@ -57,13 +64,6 @@ func decodeImageConfig(input io.Reader) (image.Config, string, error) {
|
|||||||
return image.DecodeConfig(buffer)
|
return image.DecodeConfig(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// bmpColorMask 保存一个BMP颜色分量的位域位置和取值范围
|
|
||||||
type bmpColorMask struct {
|
|
||||||
mask uint32
|
|
||||||
shift uint
|
|
||||||
max uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// sample 将位域采样值映射到八位通道,缺省透明度为不透明
|
// sample 将位域采样值映射到八位通道,缺省透明度为不透明
|
||||||
// 入参: value 编码像素
|
// 入参: value 编码像素
|
||||||
// 返回: uint8 通道值
|
// 返回: uint8 通道值
|
||||||
|
|||||||
+3
-3
@@ -25,6 +25,9 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// errTIFFRandomAccess 表示目录链需要回读,仅流式输入需重新打开资源
|
||||||
|
var errTIFFRandomAccess = errors.New("TIFF directory requires random access")
|
||||||
|
|
||||||
// tiffImageLayout 保存单页TIFF的尺寸、方向、采样及压缩参数
|
// tiffImageLayout 保存单页TIFF的尺寸、方向、采样及压缩参数
|
||||||
type tiffImageLayout struct {
|
type tiffImageLayout struct {
|
||||||
width, height int
|
width, height int
|
||||||
@@ -39,9 +42,6 @@ type tiffImageLayout struct {
|
|||||||
t4, t6 uint32
|
t4, t6 uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// errTIFFRandomAccess 表示目录链需要回读,仅流式输入需重新打开资源
|
|
||||||
var errTIFFRandomAccess = errors.New("TIFF directory requires random access")
|
|
||||||
|
|
||||||
// tiffDirectoryInput 只替换解码视图中的首目录偏移,不复制或改写原始资源
|
// tiffDirectoryInput 只替换解码视图中的首目录偏移,不复制或改写原始资源
|
||||||
type tiffDirectoryInput struct {
|
type tiffDirectoryInput struct {
|
||||||
reader *bytes.Reader
|
reader *bytes.Reader
|
||||||
|
|||||||
+45
-18
@@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package ofdgo
|
package ofdgo
|
||||||
|
|
||||||
import "encoding/xml"
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// ofdCreator 标识本库生成的文档
|
// ofdCreator 标识本库生成的文档
|
||||||
const ofdCreator = "xiaoqidun/ofdgo"
|
const ofdCreator = "xiaoqidun/ofdgo"
|
||||||
@@ -37,23 +40,6 @@ type DocBody struct {
|
|||||||
versioned bool
|
versioned bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML 读取文档入口并记录版本列表是否存在,版本描述在选择时读取
|
|
||||||
// 入参: decoder XML解码器, start 起始节点
|
|
||||||
// 返回: error 解析错误
|
|
||||||
func (body *DocBody) UnmarshalXML(decoder *xml.Decoder, start xml.StartElement) error {
|
|
||||||
type docBody DocBody
|
|
||||||
var value struct {
|
|
||||||
docBody
|
|
||||||
Versions *struct{} `xml:"Versions"`
|
|
||||||
}
|
|
||||||
if err := decoder.DecodeElement(&value, &start); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*body = DocBody(value.docBody)
|
|
||||||
body.versioned = value.Versions != nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DocInfo 文档元数据
|
// DocInfo 文档元数据
|
||||||
type DocInfo struct {
|
type DocInfo struct {
|
||||||
DocID string `xml:"DocID"`
|
DocID string `xml:"DocID"`
|
||||||
@@ -79,3 +65,44 @@ type CustomData struct {
|
|||||||
Value string `xml:",chardata"`
|
Value string `xml:",chardata"`
|
||||||
sourceIndex int
|
sourceIndex int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取主入口,区分标准文档条目与扩展中的同名节点
|
||||||
|
// 入参: decoder XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (ofd *OFD) UnmarshalXML(decoder *xml.Decoder, start xml.StartElement) error {
|
||||||
|
if start.Name.Local != "OFD" {
|
||||||
|
return fmt.Errorf("expected OFD root, got %s", start.Name.Local)
|
||||||
|
}
|
||||||
|
*ofd = OFD{XMLName: start.Name, Version: attrValue(start, "Version"), DocType: attrValue(start, "DocType")}
|
||||||
|
return decodeOFDChildren(decoder, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
if node.Name.Local != "DocBody" {
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
var body DocBody
|
||||||
|
if err := d.DecodeElement(&body, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ofd.DocBody = append(ofd.DocBody, body)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取文档入口并记录版本列表是否存在,版本描述在选择时读取
|
||||||
|
// 入参: decoder XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (body *DocBody) UnmarshalXML(decoder *xml.Decoder, start xml.StartElement) error {
|
||||||
|
*body = DocBody{}
|
||||||
|
return decodeOFDChildren(decoder, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
switch node.Name.Local {
|
||||||
|
case "DocInfo":
|
||||||
|
return d.DecodeElement(&body.DocInfo, &node)
|
||||||
|
case "DocRoot":
|
||||||
|
return d.DecodeElement(&body.DocRoot, &node)
|
||||||
|
case "Signatures":
|
||||||
|
return d.DecodeElement(&body.Signatures, &node)
|
||||||
|
case "Versions":
|
||||||
|
body.versioned = true
|
||||||
|
}
|
||||||
|
return d.Skip()
|
||||||
|
})
|
||||||
|
}
|
||||||
+11
-39
@@ -162,7 +162,7 @@ func (l *Layer) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
|||||||
l.ID = attrValue(start, "ID")
|
l.ID = attrValue(start, "ID")
|
||||||
l.Type = attrValue(start, "Type")
|
l.Type = attrValue(start, "Type")
|
||||||
l.DrawParam = attrValue(start, "DrawParam")
|
l.DrawParam = attrValue(start, "DrawParam")
|
||||||
return decodeObjectContainer(d, start, l.decodeObject)
|
return decodeOFDChildren(d, start, l.decodeObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML 解析复合图元并保留对象顺序
|
// UnmarshalXML 解析复合图元并保留对象顺序
|
||||||
@@ -199,7 +199,7 @@ func (c *CompositeGraphicUnit) UnmarshalXML(d *xml.Decoder, start xml.StartEleme
|
|||||||
c.Visible = &visible
|
c.Visible = &visible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return decodeObjectContainer(d, start, c.decodeObject)
|
return decodeOFDChildren(d, start, c.decodeObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML 解析注释外观并保留对象顺序
|
// UnmarshalXML 解析注释外观并保留对象顺序
|
||||||
@@ -208,7 +208,7 @@ func (c *CompositeGraphicUnit) UnmarshalXML(d *xml.Decoder, start xml.StartEleme
|
|||||||
func (a *Appearance) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (a *Appearance) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
*a = Appearance{}
|
*a = Appearance{}
|
||||||
a.Boundary = attrValue(start, "Boundary")
|
a.Boundary = attrValue(start, "Boundary")
|
||||||
return decodeObjectContainer(d, start, a.decodeObject)
|
return decodeOFDChildren(d, start, a.decodeObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalXML 解析图案单元内容并保留对象顺序
|
// UnmarshalXML 解析图案单元内容并保留对象顺序
|
||||||
@@ -216,7 +216,7 @@ func (a *Appearance) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error
|
|||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
func (p *PatternContent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
func (p *PatternContent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
*p = PatternContent{Thumbnail: attrValue(start, "Thumbnail")}
|
*p = PatternContent{Thumbnail: attrValue(start, "Thumbnail")}
|
||||||
return decodeObjectContainer(d, start, p.decodeObject)
|
return decodeOFDChildren(d, start, p.decodeObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
// objects 按绘制顺序访问底纹单元,Objects优先于分类数组
|
// objects 按绘制顺序访问底纹单元,Objects优先于分类数组
|
||||||
@@ -314,28 +314,6 @@ func decodeGraphicObject(d *xml.Decoder, start xml.StartElement, target graphicO
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeObjectContainer 解析图形对象容器
|
|
||||||
// 入参: d XML解码器, start 起始节点, decode 对象解码函数
|
|
||||||
// 返回: error 错误信息
|
|
||||||
func decodeObjectContainer(d *xml.Decoder, start xml.StartElement, decode func(*xml.Decoder, xml.StartElement) error) error {
|
|
||||||
for {
|
|
||||||
tok, err := d.Token()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch node := tok.(type) {
|
|
||||||
case xml.StartElement:
|
|
||||||
if err := decode(d, node); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case xml.EndElement:
|
|
||||||
if node.Name.Local == start.Name.Local {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeObject 解析图层子对象
|
// decodeObject 解析图层子对象
|
||||||
// 入参: d XML解码器, start 起始节点
|
// 入参: d XML解码器, start 起始节点
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
@@ -351,7 +329,7 @@ func (l *Layer) decodeObject(d *xml.Decoder, start xml.StartElement) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if start.Name.Local == "PageBlock" {
|
if start.Name.Local == "PageBlock" {
|
||||||
return decodeObjectContainer(d, start, l.decodeObject)
|
return decodeOFDChildren(d, start, l.decodeObject)
|
||||||
}
|
}
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -388,15 +366,9 @@ func (c *CompositeGraphicUnit) decodeObject(d *xml.Decoder, start xml.StartEleme
|
|||||||
}
|
}
|
||||||
c.Clips = &clips
|
c.Clips = &clips
|
||||||
case "Actions":
|
case "Actions":
|
||||||
var actions struct {
|
return decodeOFDActions(d, start, &c.Actions)
|
||||||
Action []Action `xml:"Action"`
|
|
||||||
}
|
|
||||||
if err := d.DecodeElement(&actions, &start); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Actions = actions.Action
|
|
||||||
case "Content", "PageBlock":
|
case "Content", "PageBlock":
|
||||||
return decodeObjectContainer(d, start, c.decodeObject)
|
return decodeOFDChildren(d, start, c.decodeObject)
|
||||||
default:
|
default:
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -418,7 +390,7 @@ func (a *Appearance) decodeObject(d *xml.Decoder, start xml.StartElement) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if start.Name.Local == "PageBlock" {
|
if start.Name.Local == "PageBlock" {
|
||||||
return decodeObjectContainer(d, start, a.decodeObject)
|
return decodeOFDChildren(d, start, a.decodeObject)
|
||||||
}
|
}
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
@@ -438,17 +410,17 @@ func (p *PatternContent) decodeObject(d *xml.Decoder, start xml.StartElement) er
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if start.Name.Local == "PageBlock" {
|
if start.Name.Local == "PageBlock" {
|
||||||
return decodeObjectContainer(d, start, p.decodeObject)
|
return decodeOFDChildren(d, start, p.decodeObject)
|
||||||
}
|
}
|
||||||
return d.Skip()
|
return d.Skip()
|
||||||
}
|
}
|
||||||
|
|
||||||
// attrValue 获取XML属性值
|
// attrValue 获取无命名空间的XML属性值
|
||||||
// 入参: start 起始节点, name 属性名
|
// 入参: start 起始节点, name 属性名
|
||||||
// 返回: string 属性值
|
// 返回: string 属性值
|
||||||
func attrValue(start xml.StartElement, name string) string {
|
func attrValue(start xml.StartElement, name string) string {
|
||||||
for _, attr := range start.Attr {
|
for _, attr := range start.Attr {
|
||||||
if attr.Name.Local == name {
|
if attr.Name.Space == "" && attr.Name.Local == name {
|
||||||
return attr.Value
|
return attr.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-1
@@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package ofdgo
|
package ofdgo
|
||||||
|
|
||||||
import "encoding/xml"
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// PageContent 页面内容
|
// PageContent 页面内容
|
||||||
type PageContent struct {
|
type PageContent struct {
|
||||||
@@ -270,3 +273,56 @@ type ImageBorder struct {
|
|||||||
DashPattern string `xml:"DashPattern,attr"`
|
DashPattern string `xml:"DashPattern,attr"`
|
||||||
BorderColor *StrokeColor `xml:"BorderColor"`
|
BorderColor *StrokeColor `xml:"BorderColor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取页面直接子节点,扩展内容不作为页面区域、资源或图层使用
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (page *PageContent) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
if start.Name.Local != "Page" {
|
||||||
|
return fmt.Errorf("expected Page root, got %s", start.Name.Local)
|
||||||
|
}
|
||||||
|
*page = PageContent{XMLName: start.Name}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
switch node.Name.Local {
|
||||||
|
case "Area":
|
||||||
|
return d.DecodeElement(&page.Area, &node)
|
||||||
|
case "Template":
|
||||||
|
var template Template
|
||||||
|
if err := d.DecodeElement(&template, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
page.Template = append(page.Template, template)
|
||||||
|
case "PageRes":
|
||||||
|
var location string
|
||||||
|
if err := d.DecodeElement(&location, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
page.PageRes = append(page.PageRes, location)
|
||||||
|
case "Content":
|
||||||
|
return d.DecodeElement(&page.Content, &node)
|
||||||
|
case "Actions":
|
||||||
|
return decodeOFDActions(d, node, &page.Actions)
|
||||||
|
default:
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 读取页面图层,不将外部命名空间中的Layer作为图层
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (content *Content) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
*content = Content{}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
if node.Name.Local != "Layer" {
|
||||||
|
return d.Skip()
|
||||||
|
}
|
||||||
|
var layer Layer
|
||||||
|
if err := d.DecodeElement(&layer, &node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
content.Layer = append(content.Layer, layer)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
+37
-6
@@ -62,6 +62,27 @@ type Reader struct {
|
|||||||
encryption *encryptionState
|
encryption *encryptionState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// documentPageList 仅读取文档中的页面索引,统计页数时不构建其他结构
|
||||||
|
type documentPageList struct {
|
||||||
|
Pages Pages
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalXML 按文档命名空间读取页面列表,跳过其他文档结构和扩展
|
||||||
|
// 入参: d XML解码器, start 起始节点
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func (list *documentPageList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
if start.Name.Local != "Document" {
|
||||||
|
return fmt.Errorf("expected Document root, got %s", start.Name.Local)
|
||||||
|
}
|
||||||
|
*list = documentPageList{}
|
||||||
|
return decodeOFDChildren(d, start, func(d *xml.Decoder, node xml.StartElement) error {
|
||||||
|
if node.Name.Local == "Pages" {
|
||||||
|
return d.DecodeElement(&list.Pages, &node)
|
||||||
|
}
|
||||||
|
return d.Skip()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Close 关闭阅读器持有的文件,重复调用不再关闭底层文件
|
// Close 关闭阅读器持有的文件,重复调用不再关闭底层文件
|
||||||
// 返回: error 错误信息
|
// 返回: error 错误信息
|
||||||
func (r *Reader) Close() error {
|
func (r *Reader) Close() error {
|
||||||
@@ -117,10 +138,7 @@ func (r *Reader) DocumentPageCountContext(ctx context.Context, index int) (int,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
var doc struct {
|
var doc documentPageList
|
||||||
XMLName xml.Name `xml:"Document"`
|
|
||||||
Pages Pages `xml:"Pages"`
|
|
||||||
}
|
|
||||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||||
if err := decoder.Decode(&doc); err != nil {
|
if err := decoder.Decode(&doc); err != nil {
|
||||||
return 0, fmt.Errorf("failed to unmarshal document.xml: %w", err)
|
return 0, fmt.Errorf("failed to unmarshal document.xml: %w", err)
|
||||||
@@ -1003,6 +1021,7 @@ func (r *Reader) readPageHeaderContext(ctx context.Context, page Page) (PageCont
|
|||||||
defer f.Close()
|
defer f.Close()
|
||||||
var content PageContent
|
var content PageContent
|
||||||
d := xml.NewDecoder(imageInput{ReadCloser: f, context: ctx})
|
d := xml.NewDecoder(imageInput{ReadCloser: f, context: ctx})
|
||||||
|
var root xml.Name
|
||||||
scan:
|
scan:
|
||||||
for {
|
for {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
@@ -1016,9 +1035,21 @@ scan:
|
|||||||
return PageContent{}, err
|
return PageContent{}, err
|
||||||
}
|
}
|
||||||
if start, ok := token.(xml.StartElement); ok {
|
if start, ok := token.(xml.StartElement); ok {
|
||||||
switch start.Name.Local {
|
if root.Local == "" {
|
||||||
case "Page":
|
if start.Name.Local != "Page" {
|
||||||
|
return PageContent{}, fmt.Errorf("expected Page root, got %s", start.Name.Local)
|
||||||
|
}
|
||||||
|
root = start.Name
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
|
if !ofdXMLChild(root, start.Name) {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return PageContent{}, err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
start = ofdXMLStart(start)
|
||||||
|
switch start.Name.Local {
|
||||||
case "Template":
|
case "Template":
|
||||||
var template Template
|
var template Template
|
||||||
if err := d.DecodeElement(&template, &start); err != nil {
|
if err := d.DecodeElement(&template, &start); err != nil {
|
||||||
|
|||||||
+3
-3
@@ -61,6 +61,9 @@ type Renderer struct {
|
|||||||
TransparentBackground bool
|
TransparentBackground bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RendererOption 渲染器配置选项
|
||||||
|
type RendererOption func(*Renderer)
|
||||||
|
|
||||||
// deviceLineWidth 按设备分辨率计算描边宽度,零线宽为一像素,正线宽至少为两像素
|
// deviceLineWidth 按设备分辨率计算描边宽度,零线宽为一像素,正线宽至少为两像素
|
||||||
// 入参: width 页面空间线宽,单位为毫米
|
// 入参: width 页面空间线宽,单位为毫米
|
||||||
// 返回: float64 设备描边宽度,单位为毫米
|
// 返回: float64 设备描边宽度,单位为毫米
|
||||||
@@ -75,9 +78,6 @@ func (r *Renderer) deviceLineWidth(width float64) float64 {
|
|||||||
return width
|
return width
|
||||||
}
|
}
|
||||||
|
|
||||||
// RendererOption 渲染器配置选项
|
|
||||||
type RendererOption func(*Renderer)
|
|
||||||
|
|
||||||
// ClearCache 释放派生字体、图片、模板与后端缓存,不改变文档和渲染配置
|
// ClearCache 释放派生字体、图片、模板与后端缓存,不改变文档和渲染配置
|
||||||
func (r *Renderer) ClearCache() {
|
func (r *Renderer) ClearCache() {
|
||||||
r.backendStates = make(map[any]any)
|
r.backendStates = make(map[any]any)
|
||||||
|
|||||||
+3
-3
@@ -19,9 +19,6 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// U3DRenderMode 定义静态几何的呈现方式,边线和顶点使用单像素覆盖
|
|
||||||
type U3DRenderMode uint8
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
U3DRenderSolid U3DRenderMode = iota // 实体
|
U3DRenderSolid U3DRenderMode = iota // 实体
|
||||||
U3DRenderSolidWireframe // 实体线框
|
U3DRenderSolidWireframe // 实体线框
|
||||||
@@ -38,6 +35,9 @@ const (
|
|||||||
U3DRenderShadedIllustration // 着色插图
|
U3DRenderShadedIllustration // 着色插图
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// U3DRenderMode 定义静态几何的呈现方式,边线和顶点使用单像素覆盖
|
||||||
|
type U3DRenderMode uint8
|
||||||
|
|
||||||
// u3dPrimitiveKey 区分共享点线的颜色及混合状态,避免透明边点重复叠加
|
// u3dPrimitiveKey 区分共享点线的颜色及混合状态,避免透明边点重复叠加
|
||||||
type u3dPrimitiveKey struct {
|
type u3dPrimitiveKey struct {
|
||||||
a, b [7]float64
|
a, b [7]float64
|
||||||
|
|||||||
+60
-1
@@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package ofdgo
|
package ofdgo
|
||||||
|
|
||||||
import "unicode/utf8"
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
// 旧版与2016版OFD命名空间
|
// 旧版与2016版OFD命名空间
|
||||||
const (
|
const (
|
||||||
@@ -33,6 +36,62 @@ const (
|
|||||||
// ofdXMLNamespace 标识XML命名空间,不代表文档版本或功能支持范围
|
// ofdXMLNamespace 标识XML命名空间,不代表文档版本或功能支持范围
|
||||||
type ofdXMLNamespace uint8
|
type ofdXMLNamespace uint8
|
||||||
|
|
||||||
|
// ofdXMLChild 判断子节点是否属于可识别的OFD命名空间或父节点所在的未知命名空间
|
||||||
|
// 入参: parent 父节点名称, child 子节点名称
|
||||||
|
// 返回: bool 是否属于同一OFD结构
|
||||||
|
func ofdXMLChild(parent, child xml.Name) bool {
|
||||||
|
return classifyOFDNamespace(child.Space) != ofdXMLUnknown || child.Space == parent.Space
|
||||||
|
}
|
||||||
|
|
||||||
|
// ofdXMLStart 保留OFD节点的无命名空间属性,不修改解码器返回的属性切片
|
||||||
|
// 入参: start 起始节点
|
||||||
|
// 返回: xml.StartElement 供标准结构解码使用的节点
|
||||||
|
func ofdXMLStart(start xml.StartElement) xml.StartElement {
|
||||||
|
for i, attr := range start.Attr {
|
||||||
|
if attr.Name.Space == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
attrs := make([]xml.Attr, i, len(start.Attr)-1)
|
||||||
|
copy(attrs, start.Attr[:i])
|
||||||
|
for _, attr := range start.Attr[i+1:] {
|
||||||
|
if attr.Name.Space == "" {
|
||||||
|
attrs = append(attrs, attr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start.Attr = attrs
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return start
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeOFDChildren 按命名空间解析直接子节点,跳过外部扩展而不提升其内部节点
|
||||||
|
// 入参: d XML解码器, start 容器起始节点, decode 子节点解码函数
|
||||||
|
// 返回: error 解析错误
|
||||||
|
func decodeOFDChildren(d *xml.Decoder, start xml.StartElement, decode func(*xml.Decoder, xml.StartElement) error) error {
|
||||||
|
for {
|
||||||
|
token, err := d.Token()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch node := token.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
if !ofdXMLChild(start.Name, node.Name) {
|
||||||
|
if err := d.Skip(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := decode(d, ofdXMLStart(node)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case xml.EndElement:
|
||||||
|
if node.Name == start.Name {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// classifyOFDNamespace 识别OFD命名空间,未声明与未知版本分别处理
|
// classifyOFDNamespace 识别OFD命名空间,未声明与未知版本分别处理
|
||||||
// 入参: namespace XML命名空间
|
// 入参: namespace XML命名空间
|
||||||
// 返回: ofdXMLNamespace 命名空间类别
|
// 返回: ofdXMLNamespace 命名空间类别
|
||||||
|
|||||||
Reference in new issue
Block a user