mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 16:59:27 +08:00
579 lines
19 KiB
Go
579 lines
19 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 (
|
|
"bytes"
|
|
"context"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"slices"
|
|
"strconv"
|
|
)
|
|
|
|
// LayerInfo 描述普通页面图层,Order为同类型零基顺序,ObjectCount包含页块中的图元。
|
|
type LayerInfo struct {
|
|
ID string
|
|
Type string
|
|
DrawParam string
|
|
Order int
|
|
ObjectCount int
|
|
}
|
|
|
|
// Layers 按文件顺序返回普通页面图层,省略的类型规范化为Body,不包含模板图层。
|
|
// 入参: page 零基页面索引
|
|
// 返回: []LayerInfo 独立图层清单, error 页面读取错误
|
|
func (e *Editor) Layers(page int) ([]LayerInfo, error) {
|
|
p, err := e.page(page)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return editorLayerInfos(p), nil
|
|
}
|
|
|
|
// Layers 直接读取普通页面图层,不创建编辑器或分配新标识,返回值不持有页面可变数据。
|
|
// 入参: page 零基页面索引
|
|
// 返回: []LayerInfo 按文件顺序排列的图层, error 页面读取错误
|
|
func (r *Reader) Layers(page int) ([]LayerInfo, error) {
|
|
p, err := r.PageContentByIndex(page)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return editorLayerInfos(p), nil
|
|
}
|
|
|
|
// editorLayerInfos 将页面图层投影为不共享可变容器的管理清单。
|
|
// 入参: p 已读取的页面
|
|
// 返回: []LayerInfo 图层清单
|
|
func editorLayerInfos(p *PageContent) []LayerInfo {
|
|
result := make([]LayerInfo, 0, len(p.Content.Layer))
|
|
orders := make(map[string]int)
|
|
for _, layer := range p.Content.Layer {
|
|
kind := editorLayerType(layer.Type)
|
|
result = append(result, LayerInfo{layer.ID, kind, layer.DrawParam, orders[kind], len(layer.Objects)})
|
|
orders[kind]++
|
|
}
|
|
return result
|
|
}
|
|
|
|
// AddLayer 追加空图层并返回新标识,空类型采用Body,空DrawParam表示不设置继承参数。
|
|
// 入参: page 页面索引, layerType 图层类型, drawParam 绘制参数标识
|
|
// 返回: string 新图层标识, error 类型、资源或结构错误
|
|
func (e *Editor) AddLayer(page int, layerType, drawParam string) (string, error) {
|
|
layerType = editorLayerType(layerType)
|
|
if !validEditorLayerType(layerType) {
|
|
return "", fmt.Errorf("invalid layer type %q", layerType)
|
|
}
|
|
var id string
|
|
err := e.editLayerPage(page, func(r *Reader, data []byte, root *editorXML) ([]byte, error) {
|
|
if err := e.validateLayerDrawParam(page, drawParam); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := e.prepareSourceIDs(); err != nil {
|
|
return nil, err
|
|
}
|
|
id = e.nextID()
|
|
attrs := ofdAttrs{{Name: xml.Name{Local: "ID"}, Value: id}, {Name: xml.Name{Local: "Type"}, Value: layerType}}
|
|
attrs.add("DrawParam", drawParam)
|
|
content := root.child("Content")
|
|
fragment, err := root.containerXML("Layer", attrs, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if content != nil {
|
|
return editorAppendStructure(data, content, fragment), nil
|
|
}
|
|
fragment, err = root.containerXML("Content", nil, fragment)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
at := root.close
|
|
if actions := root.child("Actions"); actions != nil {
|
|
at = actions.start
|
|
}
|
|
if root.open == root.end {
|
|
return editorAppendStructure(data, root, fragment), nil
|
|
}
|
|
return editorPatchXML(data, []editorXMLPatch{{at, at, fragment}}), nil
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return id, nil
|
|
}
|
|
|
|
// SetLayerType 设置图层类型,不改变图元坐标及绘制参数;同类型顺序由文件位置决定。
|
|
// 入参: page 页面索引, id 图层标识, layerType 图层类型
|
|
// 返回: error 标识、类型或结构错误
|
|
func (e *Editor) SetLayerType(page int, id, layerType string) error {
|
|
layerType = editorLayerType(layerType)
|
|
if !validEditorLayerType(layerType) {
|
|
return fmt.Errorf("invalid layer type %q", layerType)
|
|
}
|
|
return e.editLayerPage(page, func(_ *Reader, data []byte, root *editorXML) ([]byte, error) {
|
|
node, err := editorFindStructureID(root.child("Content"), "Layer", "ID", id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if editorLayerType(node.attr("Type")) == layerType {
|
|
return data, nil
|
|
}
|
|
return editorStructureAttribute(data, node, "Type", layerType)
|
|
})
|
|
}
|
|
|
|
// SetLayerDrawParam 设置图层绘制参数,空标识清除引用,图元局部覆盖保持不变。
|
|
// 入参: page 页面索引, id 图层标识, drawParam 绘制参数标识
|
|
// 返回: error 标识、资源或结构错误
|
|
func (e *Editor) SetLayerDrawParam(page int, id, drawParam string) error {
|
|
return e.editLayerPage(page, func(_ *Reader, data []byte, root *editorXML) ([]byte, error) {
|
|
node, err := editorFindStructureID(root.child("Content"), "Layer", "ID", id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := e.validateLayerDrawParam(page, drawParam); err != nil {
|
|
return nil, err
|
|
}
|
|
if node.attr("DrawParam") == drawParam {
|
|
return data, nil
|
|
}
|
|
return editorStructureAttribute(data, node, "DrawParam", drawParam)
|
|
})
|
|
}
|
|
|
|
// MoveLayer 调整图层在同类型图层中的零基顺序,保留其他类型及扩展节点的位置。
|
|
// 入参: page 页面索引, id 图层标识, order 同类型目标顺序
|
|
// 返回: error 标识、顺序或结构错误
|
|
func (e *Editor) MoveLayer(page int, id string, order int) error {
|
|
return e.editLayerPage(page, func(_ *Reader, data []byte, root *editorXML) ([]byte, error) {
|
|
node, err := editorFindStructureID(root.child("Content"), "Layer", "ID", id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var nodes []*editorXML
|
|
for _, child := range root.child("Content").children {
|
|
if child.matchesOFD("Layer") && editorLayerType(child.attr("Type")) == editorLayerType(node.attr("Type")) {
|
|
nodes = append(nodes, child)
|
|
}
|
|
}
|
|
return editorMoveStructure(data, nodes, node, order)
|
|
})
|
|
}
|
|
|
|
// DeleteLayer 删除空图层,含图元、页块或扩展子元素时拒绝;可先在Transaction中迁移图元。
|
|
// 入参: page 页面索引, id 图层标识
|
|
// 返回: error 标识、非空图层或结构错误
|
|
func (e *Editor) DeleteLayer(page int, id string) error {
|
|
return e.editLayerPage(page, func(_ *Reader, data []byte, root *editorXML) ([]byte, error) {
|
|
content := root.child("Content")
|
|
node, err := editorFindStructureID(content, "Layer", "ID", id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(node.children) != 0 {
|
|
return nil, fmt.Errorf("layer %q is not empty", id)
|
|
}
|
|
if len(content.children) == 1 && len(content.attrs) == 0 {
|
|
return editorPatchXML(data, []editorXMLPatch{{content.start, content.end, nil}}), nil
|
|
}
|
|
if len(editorStructureChildren(content, "Layer")) == 1 {
|
|
return nil, fmt.Errorf("last layer contains a preserved Content container")
|
|
}
|
|
return editorPatchXML(data, []editorXMLPatch{{node.start, node.end, nil}}), nil
|
|
})
|
|
}
|
|
|
|
// MoveObjectsToLayer 将普通页面选区按原绘制顺序追加到目标层,保持标识、坐标、扩展及有效样式。
|
|
// 不接受模板或注解标识;页块成员须先通过已有复合编辑接口处理,失败不产生部分移动。
|
|
// 入参: page 页面索引, ids 不重复的普通图元标识, layerID 目标图层标识
|
|
// 返回: error 选区、继承参数或结构错误
|
|
func (e *Editor) MoveObjectsToLayer(page int, ids []string, layerID string) error {
|
|
return e.editLayerPage(page, func(_ *Reader, data []byte, root *editorXML) ([]byte, error) {
|
|
target, err := editorFindStructureID(root.child("Content"), "Layer", "ID", layerID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
wanted := make(map[string]bool)
|
|
for _, id := range ids {
|
|
key := editorResourceID(id)
|
|
if key == "" || wanted[key] {
|
|
return nil, fmt.Errorf("invalid or duplicate object ID %q", id)
|
|
}
|
|
wanted[key] = true
|
|
}
|
|
var patches []editorXMLPatch
|
|
var added []byte
|
|
layers := editorStructureChildren(root.child("Content"), "Layer")
|
|
slices.SortStableFunc(layers, func(a, b *editorXML) int { return layerOrder(a.attr("Type")) - layerOrder(b.attr("Type")) })
|
|
for _, layer := range layers {
|
|
nodes := make(map[string]*editorXML)
|
|
if err := editorXMLObjects(layer, nodes); err != nil {
|
|
return nil, err
|
|
}
|
|
ordered := make([]*editorXML, 0, len(nodes))
|
|
for _, node := range nodes {
|
|
ordered = append(ordered, node)
|
|
}
|
|
slices.SortFunc(ordered, func(a, b *editorXML) int { return a.start - b.start })
|
|
for _, node := range ordered {
|
|
key := editorResourceID(node.attr("ID"))
|
|
if !wanted[key] {
|
|
continue
|
|
}
|
|
delete(wanted, key)
|
|
if layer == target {
|
|
continue
|
|
}
|
|
if node.parent != layer {
|
|
return nil, fmt.Errorf("object %q belongs to a PageBlock", key)
|
|
}
|
|
fragment, err := editorXMLStandalone(data[node.start:node.end], node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if layer.attr("DrawParam") != target.attr("DrawParam") {
|
|
member, err := newEditorCompositeNode(fragment)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
member.drawParams = []string{layer.attr("DrawParam")}
|
|
member.defaults, err = e.editorDrawParam(layer.attr("DrawParam"), make(map[string]bool))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if member.object.Type == "TextObject" || member.object.Type == "ImageObject" {
|
|
styled, err := e.compositeMemberStyle(member)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := member.update(styled); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if err := e.isolateCompositeStyle(member); err != nil {
|
|
return nil, err
|
|
}
|
|
fragment, _, err = e.writeCompositeNode(member)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
added = append(added, fragment...)
|
|
patches = append(patches, editorXMLPatch{node.start, node.end, nil})
|
|
}
|
|
}
|
|
if len(wanted) != 0 {
|
|
return nil, fmt.Errorf("selection contains missing ordinary page objects")
|
|
}
|
|
if len(added) == 0 {
|
|
return data, nil
|
|
}
|
|
if target.open == target.end {
|
|
patches = append(patches, editorXMLContent(data, target, added))
|
|
} else {
|
|
patches = append(patches, editorXMLPatch{target.close, target.close, added})
|
|
}
|
|
return editorPatchXML(data, patches), nil
|
|
})
|
|
}
|
|
|
|
// editorLayerType 将标准省略类型规范化,不改变已明确指定的类型。
|
|
// 入参: value 原类型
|
|
// 返回: string 有效类型
|
|
func editorLayerType(value string) string {
|
|
if value == "" {
|
|
return "Body"
|
|
}
|
|
return value
|
|
}
|
|
|
|
// validEditorLayerType 按正文表15校验可管理的图层类型。
|
|
// 入参: value 类型值
|
|
// 返回: bool 是否为支持的标准类型
|
|
func validEditorLayerType(value string) bool {
|
|
return value == "Body" || value == "Background" || value == "Foreground"
|
|
}
|
|
|
|
// validateLayerDrawParam 在目标页面资源环境中核验完整绘制参数继承链。
|
|
// 入参: page 页面索引, id 绘制参数标识
|
|
// 返回: error 页面或资源错误
|
|
func (e *Editor) validateLayerDrawParam(page int, id string) error {
|
|
if _, err := e.page(page); err != nil {
|
|
return err
|
|
}
|
|
_, err := e.editorDrawParam(id, make(map[string]bool))
|
|
return err
|
|
}
|
|
|
|
// editLayerPage 在完整包快照上编辑页面原文,事务失败回滚,提交时隔离共享页面及历史版本。
|
|
// 入参: page 页面索引, edit 页面原文修改回调
|
|
// 返回: error 读取、修改或提交错误
|
|
func (e *Editor) editLayerPage(page int, edit func(*Reader, []byte, *editorXML) ([]byte, error)) error {
|
|
return e.Transaction(func(_ *Editor) error {
|
|
if _, err := e.page(page); err != nil {
|
|
return err
|
|
}
|
|
r, err := e.Reader()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer r.Close()
|
|
doc, err := r.Doc()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
name := r.ResPath(doc.Pages.Page[page].BaseLoc)
|
|
data, err := r.readFile(name)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
root, err := parseEditorXML(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
next, err := edit(r, data, root)
|
|
if err != nil || bytes.Equal(data, next) {
|
|
return err
|
|
}
|
|
parts := map[string][]byte{name: next}
|
|
shared := slices.ContainsFunc(doc.CommonData.TemplatePage, func(ref TemplatePage) bool { return r.ResPath(ref.BaseLoc) == name })
|
|
for i, ref := range doc.Pages.Page {
|
|
shared = shared || i != page && r.ResPath(ref.BaseLoc) == name
|
|
}
|
|
if shared {
|
|
private := packageAvailableName(r, parts, name)
|
|
delete(parts, name)
|
|
parts[private] = next
|
|
document, err := r.readFile(r.documentRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
entry, err := parseEditorXML(document)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
refNode := entry.child("Pages").childAt("Page", page)
|
|
parts[r.documentRoot], err = editorStructureAttribute(document, refNode, "BaseLoc", "/"+private)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return e.commitLayerTemplateParts(r, parts)
|
|
})
|
|
}
|
|
|
|
// editorStructureChildren 仅枚举当前标准命名空间中的直接子元素。
|
|
// 入参: root 容器节点, kind 子元素名称
|
|
// 返回: []*editorXML 文件顺序节点清单
|
|
func editorStructureChildren(root *editorXML, kind string) []*editorXML {
|
|
var nodes []*editorXML
|
|
if root != nil {
|
|
for _, node := range root.children {
|
|
if node.matchesOFD(kind) {
|
|
nodes = append(nodes, node)
|
|
}
|
|
}
|
|
}
|
|
return nodes
|
|
}
|
|
|
|
// editorFindStructureID 按数值标识查找唯一直接子元素,拒绝缺失及歧义标识。
|
|
// 入参: root 容器节点, kind 元素名称, attribute 标识属性名, id 标识值
|
|
// 返回: *editorXML 唯一节点, error 标识错误
|
|
func editorFindStructureID(root *editorXML, kind, attribute, id string) (*editorXML, error) {
|
|
var found *editorXML
|
|
if editorResourceID(id) == "" {
|
|
return nil, fmt.Errorf("invalid %s ID %q", kind, id)
|
|
}
|
|
for _, node := range editorStructureChildren(root, kind) {
|
|
if sameResourceID(node.attr(attribute), id) {
|
|
if found != nil {
|
|
return nil, fmt.Errorf("ambiguous %s ID %q", kind, id)
|
|
}
|
|
found = node
|
|
}
|
|
}
|
|
if found == nil {
|
|
return nil, fmt.Errorf("%s %q not found", kind, id)
|
|
}
|
|
return found, nil
|
|
}
|
|
|
|
// editorStructureAttribute 局部替换或删除无命名空间属性,保留扩展及其余原文。
|
|
// 入参: data 原文, node 目标节点, name 属性名, value 新值,空值删除属性
|
|
// 返回: []byte 更新后的完整原文, error XML错误
|
|
func editorStructureAttribute(data []byte, node *editorXML, name, value string) ([]byte, error) {
|
|
var desired *string
|
|
if value != "" {
|
|
desired = &value
|
|
}
|
|
patch, err := editorXMLAttributeChange(data, node, name, desired)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return editorPatchXML(data, []editorXMLPatch{patch}), nil
|
|
}
|
|
|
|
// editorAppendStructure 向普通或自闭合容器追加已绑定命名空间的元素。
|
|
// 入参: data 原文, node 容器节点, fragment 待追加片段
|
|
// 返回: []byte 更新后的完整原文
|
|
func editorAppendStructure(data []byte, node *editorXML, fragment []byte) []byte {
|
|
if node.open == node.end {
|
|
return editorPatchXML(data, []editorXMLPatch{editorXMLContent(data, node, fragment)})
|
|
}
|
|
return editorPatchXML(data, []editorXMLPatch{{node.close, node.close, fragment}})
|
|
}
|
|
|
|
// editorMoveStructure 在指定同类节点位置间移动完整原文,不触及夹杂的扩展元素。
|
|
// 入参: data 原文, nodes 同类节点, node 被移动节点, order 目标零基顺序
|
|
// 返回: []byte 更新后的完整原文, error 顺序错误
|
|
func editorMoveStructure(data []byte, nodes []*editorXML, node *editorXML, order int) ([]byte, error) {
|
|
if order < 0 || order >= len(nodes) {
|
|
return nil, fmt.Errorf("order %d out of range", order)
|
|
}
|
|
from := slices.Index(nodes, node)
|
|
if from == order {
|
|
return data, nil
|
|
}
|
|
ordered := slices.Clone(nodes)
|
|
moveEditorItem(ordered, from, order)
|
|
patches := make([]editorXMLPatch, len(nodes))
|
|
for i, old := range nodes {
|
|
next := ordered[i]
|
|
patches[i] = editorXMLPatch{old.start, old.end, data[next.start:next.end]}
|
|
}
|
|
return editorPatchXML(data, patches), nil
|
|
}
|
|
|
|
// commitLayerTemplateParts 持久化结构与已注册资源,隔离共享文件并恢复未变图元的会话编辑状态。
|
|
// 入参: reader 当前独立包快照, parts 文件修改集
|
|
// 返回: error 资源、隔离或提交错误
|
|
func (e *Editor) commitLayerTemplateParts(reader *Reader, parts map[string][]byte) error {
|
|
ctx := context.Background()
|
|
data := parts[reader.documentRoot]
|
|
var err error
|
|
if data == nil {
|
|
data, err = reader.readFile(reader.documentRoot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
root, err := parseEditorXML(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
common := root.child("CommonData")
|
|
if common == nil {
|
|
return fmt.Errorf("document has no CommonData")
|
|
}
|
|
var definitions []byte
|
|
registered := make(map[string]bool)
|
|
for _, kind := range []string{"PublicRes", "DocumentRes"} {
|
|
for _, node := range editorStructureChildren(common, kind) {
|
|
registered[reader.ResPath(editorImportText(data, node))] = true
|
|
}
|
|
}
|
|
var fonts, images []editorResource
|
|
var spaces []ColorSpace
|
|
for _, resource := range e.resources {
|
|
if resource.name != "" {
|
|
parts[resource.name] = resource.data
|
|
}
|
|
if resource.definition() != "" && !registered[resource.name] {
|
|
definitions = append(definitions, common.textXML("DocumentRes", "/"+resource.name)...)
|
|
registered[resource.name] = true
|
|
}
|
|
if resource.font != nil {
|
|
fonts = append(fonts, resource)
|
|
}
|
|
if resource.image != nil {
|
|
images = append(images, resource)
|
|
}
|
|
if resource.space != nil {
|
|
spaces = append(spaces, *resource.space)
|
|
}
|
|
}
|
|
if len(fonts)+len(images)+len(spaces) > 0 {
|
|
encoded, err := e.encodeXML(func(x *ofdXML) { x.resources(fonts, images, spaces) })
|
|
if err != nil {
|
|
return err
|
|
}
|
|
doc, err := reader.Doc()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
name, added, err := mergePackageResources(ctx, reader, parts, doc, e.packageDirectory(), encoded)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if added {
|
|
definitions = append(definitions, common.textXML("DocumentRes", "/"+name)...)
|
|
}
|
|
}
|
|
var patches []editorXMLPatch
|
|
if maximum := common.child("MaxUnitID"); maximum != nil {
|
|
value, _ := strconv.Atoi(editorImportText(data, maximum))
|
|
if e.maxID > value {
|
|
patches = append(patches, editorXMLPatch{maximum.start, maximum.end, maximum.textXML("MaxUnitID", strconv.Itoa(e.maxID))})
|
|
}
|
|
}
|
|
if len(definitions) > 0 {
|
|
at := common.close
|
|
for _, node := range common.children {
|
|
if node.matchesOFD("TemplatePage") || node.matchesOFD("DefaultCS") {
|
|
at = node.start
|
|
break
|
|
}
|
|
}
|
|
patches = append(patches, editorXMLPatch{at, at, definitions})
|
|
}
|
|
parts[reader.documentRoot] = editorPatchXML(data, patches)
|
|
scan := &editorFontDocument{reader: reader, index: reader.DocumentIndex()}
|
|
if err := scan.apply(ctx, parts); err != nil {
|
|
return err
|
|
}
|
|
previous := e.transactionSnapshot()
|
|
if err := e.commitResourceSnapshot(ctx, reader); err != nil {
|
|
return err
|
|
}
|
|
for i := range e.pages {
|
|
oldIndex := slices.IndexFunc(previous.pages, func(p PageContent) bool { return p.ID == e.pages[i].ID })
|
|
if oldIndex < 0 || len(previous.pages[oldIndex].Content.Layer) == 0 {
|
|
continue
|
|
}
|
|
states := make(map[string]GraphicObject)
|
|
for _, layer := range previous.pages[oldIndex].Content.Layer {
|
|
for _, object := range layer.Objects {
|
|
states[editorObjectID(object)] = object
|
|
}
|
|
}
|
|
page, err := e.page(i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for j := range page.Content.Layer {
|
|
for k := range page.Content.Layer[j].Objects {
|
|
object := &page.Content.Layer[j].Objects[k]
|
|
if old, ok := states[editorObjectID(*object)]; ok {
|
|
object.state = old.state
|
|
object.TextObject.layout = old.TextObject.layout
|
|
object.CompositeGraphicUnit.states = old.CompositeGraphicUnit.states
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|