mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 08:46:55 +08:00
1 parent
413d41c31d
commit
f1864952ad
10 files changed
+450
-97
No files matched your search
+38
-1
@@ -4828,6 +4828,9 @@ async function changeDocument(name, item, ...args) {
|
|||||||
openSeq = advanceDocumentSequence();
|
openSeq = advanceDocumentSequence();
|
||||||
if (changingFont) {
|
if (changingFont) {
|
||||||
await refreshFontBinding(doc, openSeq);
|
await refreshFontBinding(doc, openSeq);
|
||||||
|
} else if (restoring && !doc.changes?.all && doc.changes?.pages?.length === 1 && doc.changes.pages[0] === state.pageIndex && (restoredIndex < 0 || restoredIndex === state.pageIndex)) {
|
||||||
|
if (location) canvasEditor.pendingSelection = {index:state.pageIndex, ids:location.ids};
|
||||||
|
await refreshEditorPage(doc, state.pageIndex, openSeq, clearSelection);
|
||||||
} else if (item || name === "ofdgoPasteObjects" || inserting || editingAnnotation) {
|
} else if (item || name === "ofdgoPasteObjects" || inserting || editingAnnotation) {
|
||||||
await refreshEditorPage(doc, item ? item.index : args[0], openSeq, clearSelection);
|
await refreshEditorPage(doc, item ? item.index : args[0], openSeq, clearSelection);
|
||||||
} else {
|
} else {
|
||||||
@@ -4960,7 +4963,7 @@ async function refreshFontBinding(doc, openSeq) {
|
|||||||
|
|
||||||
async function refreshEditorPage(doc, index, openSeq, clearSelection = false) {
|
async function refreshEditorPage(doc, index, openSeq, clearSelection = false) {
|
||||||
state.doc = doc;
|
state.doc = doc;
|
||||||
resetSearch();
|
const search = refreshSearchPages([index], openSeq);
|
||||||
state.documentSelection = null;
|
state.documentSelection = null;
|
||||||
pageView.resetPageLoading();
|
pageView.resetPageLoading();
|
||||||
try {
|
try {
|
||||||
@@ -4997,6 +5000,7 @@ async function refreshEditorPage(doc, index, openSeq, clearSelection = false) {
|
|||||||
renderMeta(true);
|
renderMeta(true);
|
||||||
updateControls();
|
updateControls();
|
||||||
loadDocumentDetails(openSeq);
|
loadDocumentDetails(openSeq);
|
||||||
|
await search;
|
||||||
for (const shell of el.svgHost.children) pageView.observeFlowPage(shell);
|
for (const shell of el.svgHost.children) pageView.observeFlowPage(shell);
|
||||||
for (const button of el.pageList.children) pageView.observeThumbnail(button, Number(button.dataset.pageIndex), openSeq);
|
for (const button of el.pageList.children) pageView.observeThumbnail(button, Number(button.dataset.pageIndex), openSeq);
|
||||||
}
|
}
|
||||||
@@ -10771,6 +10775,39 @@ function resetSearch(clearInput = true) {
|
|||||||
clearSearchHighlights();
|
clearSearchHighlights();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function refreshSearchPages(pages, openSeq) {
|
||||||
|
const tab = documentTab(), target = tab.state, query = target.searchQuery;
|
||||||
|
if (!query || target.searchRunning) { resetSearch(false); return; }
|
||||||
|
const seq = ++target.searchSeq;
|
||||||
|
target.searchRunning = true;
|
||||||
|
const selected = target.searchMatches[target.searchIndex];
|
||||||
|
try {
|
||||||
|
const matches = target.searchMatches.filter(match => !pages.includes(match.page));
|
||||||
|
for (const page of pages) {
|
||||||
|
const result = await callTabWASM(tab, "ofdgoSearchPage", page, query);
|
||||||
|
if (tab.closed || target.openSeq !== openSeq || target.searchSeq !== seq) return;
|
||||||
|
for (const match of result || []) matches.push({...match, page});
|
||||||
|
}
|
||||||
|
matches.sort((a,b) => a.page - b.page);
|
||||||
|
target.searchMatches = matches;
|
||||||
|
target.searchIndex = selected ? matches.indexOf(selected) : -1;
|
||||||
|
if (isForegroundDocument(tab)) {
|
||||||
|
el.searchResults.replaceChildren();
|
||||||
|
clearSearchHighlights();
|
||||||
|
el.searchStatus.textContent = matches.length ? "搜索完成" : "暂无结果";
|
||||||
|
renderSearchResults();
|
||||||
|
if (target.searchIndex >= 0) renderSearchHighlights(matches[target.searchIndex].page);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (target.openSeq === openSeq && target.searchSeq === seq) {
|
||||||
|
target.searchQuery = "";
|
||||||
|
if (isForegroundDocument(tab)) { resetSearch(false); showError(err, false); }
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (target.searchSeq === seq) target.searchRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function searchDocument() {
|
async function searchDocument() {
|
||||||
if (!state.doc || document.body.hasAttribute("data-busy")) {
|
if (!state.doc || document.body.hasAttribute("data-busy")) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
+26
-6
@@ -365,11 +365,12 @@ type editorClipboard struct {
|
|||||||
// editorInfo 编辑文档信息与操作状态
|
// editorInfo 编辑文档信息与操作状态
|
||||||
type editorInfo struct {
|
type editorInfo struct {
|
||||||
DocumentInfo
|
DocumentInfo
|
||||||
Revision uint64 `json:"revision"`
|
Revision uint64 `json:"revision"`
|
||||||
CanUndo bool `json:"canUndo"`
|
CanUndo bool `json:"canUndo"`
|
||||||
CanRedo bool `json:"canRedo"`
|
CanRedo bool `json:"canRedo"`
|
||||||
PageCapabilities []uint `json:"pageCapabilities"`
|
PageCapabilities []uint `json:"pageCapabilities"`
|
||||||
EditWarnings []string `json:"editWarnings,omitempty"`
|
EditWarnings []string `json:"editWarnings,omitempty"`
|
||||||
|
Changes ofdgo.EditorChanges `json:"changes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// editorPageInfo 页面操作结果与目标页面
|
// editorPageInfo 页面操作结果与目标页面
|
||||||
@@ -3203,7 +3204,7 @@ func (e *businessEngine) encodeResult(result apiResult) string {
|
|||||||
// editorSummary 获取当前编辑文档状态
|
// editorSummary 获取当前编辑文档状态
|
||||||
// 返回: editorInfo 文档信息
|
// 返回: editorInfo 文档信息
|
||||||
func (e *businessEngine) editorSummary() editorInfo {
|
func (e *businessEngine) editorSummary() editorInfo {
|
||||||
info := editorInfo{DocumentInfo: e.currentSession.Summary(), Revision: e.currentEditor.Revision(), CanUndo: e.currentEditor.CanUndo(), CanRedo: e.currentEditor.CanRedo()}
|
info := editorInfo{DocumentInfo: e.currentSession.Summary(), Revision: e.currentEditor.Revision(), CanUndo: e.currentEditor.CanUndo(), CanRedo: e.currentEditor.CanRedo(), Changes: e.currentEditor.Changes()}
|
||||||
if !e.currentSession.doc.Permissions.Edit {
|
if !e.currentSession.doc.Permissions.Edit {
|
||||||
info.EditWarnings = append(info.EditWarnings, "原文件声明不允许编辑")
|
info.EditWarnings = append(info.EditWarnings, "原文件声明不允许编辑")
|
||||||
}
|
}
|
||||||
@@ -6741,6 +6742,25 @@ func (e *businessEngine) previewEditor(editor *ofdgo.Editor, annotations bool) (
|
|||||||
session.Renderer.SetFontFS(session.fontFS)
|
session.Renderer.SetFontFS(session.fontFS)
|
||||||
editor.SetFontFS(session.fontFS)
|
editor.SetFontFS(session.fontFS)
|
||||||
}
|
}
|
||||||
|
changes := editor.Changes()
|
||||||
|
if editor == e.currentEditor && !changes.All && len(session.boxCache) == len(e.currentSession.boxCache) {
|
||||||
|
changed := changes.Pages
|
||||||
|
for index, page := range e.currentSession.pageCache {
|
||||||
|
if !slices.Contains(changed, index) {
|
||||||
|
session.pageCache[index] = page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, index := range e.currentSession.textOrder {
|
||||||
|
if !slices.Contains(changed, index) {
|
||||||
|
session.cachePageText(index, e.currentSession.textCache[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for index, box := range e.currentSession.boxCache {
|
||||||
|
if !slices.Contains(changed, index) {
|
||||||
|
session.boxCache[index] = box
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if editor == e.currentEditor {
|
if editor == e.currentEditor {
|
||||||
session.input, e.currentSession.input = e.currentSession.input, nil
|
session.input, e.currentSession.input = e.currentSession.input, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ type outputOptimization struct {
|
|||||||
protected bool
|
protected bool
|
||||||
completed int
|
completed int
|
||||||
deflater *flate.Writer
|
deflater *flate.Writer
|
||||||
|
resources *editorResourceSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
// outputCompressionBuffer 限制候选ZIP编码,不能缩小时终止本次压缩
|
// outputCompressionBuffer 限制候选ZIP编码,不能缩小时终止本次压缩
|
||||||
|
|||||||
+37
-39
@@ -46,38 +46,41 @@ const editorPathValidationLimit = 1024
|
|||||||
// 阶段可重复,准备完成不代表保存成功
|
// 阶段可重复,准备完成不代表保存成功
|
||||||
// 回调不可重入修改编辑器,可返回context.Canceled等调用方停止原因
|
// 回调不可重入修改编辑器,可返回context.Canceled等调用方停止原因
|
||||||
type Editor struct {
|
type Editor struct {
|
||||||
OnWriteProgress func(stage string, completed, total int) error
|
OnWriteProgress func(stage string, completed, total int) error
|
||||||
Info DocInfo
|
Info DocInfo
|
||||||
pages []PageContent
|
pages []PageContent
|
||||||
resources []editorResource
|
resources []editorResource
|
||||||
runtimeResources *Resources
|
runtimeResources *Resources
|
||||||
fonts map[string]*FontResource
|
fonts map[string]*FontResource
|
||||||
fontRegistration editorFontRegistration
|
fontRegistration editorFontRegistration
|
||||||
fontDirs []string
|
fontDirs []string
|
||||||
fontFS []fs.FS
|
fontFS []fs.FS
|
||||||
fontRenderer *Renderer
|
fontRenderer *Renderer
|
||||||
fontSourcesCache *fontSourceCache
|
fontSourcesCache *fontSourceCache
|
||||||
fontMetrics map[string]FontMetrics
|
fontMetrics map[string]FontMetrics
|
||||||
backends RenderBackends
|
backends RenderBackends
|
||||||
images map[string]image.Point
|
images map[string]image.Point
|
||||||
resourceID map[editorResourceKey]string
|
resourceID map[editorResourceKey]string
|
||||||
maxID int
|
maxID int
|
||||||
history []editorChange
|
history []editorChange
|
||||||
historyIndex int
|
historyIndex int
|
||||||
historyLimit int
|
historyLimit int
|
||||||
revision uint64
|
revision uint64
|
||||||
serial uint64
|
serial uint64
|
||||||
source *editorSource
|
source *editorSource
|
||||||
origins map[string]*editorObjectOrigin
|
origins map[string]*editorObjectOrigin
|
||||||
removedPages map[string]bool
|
removedPages map[string]bool
|
||||||
structureRemaps map[string][]map[string]string
|
structureRemaps map[string][]map[string]string
|
||||||
outlines []byte
|
outlines []byte
|
||||||
encryption *encryptionState
|
encryption *encryptionState
|
||||||
output *outputOptimization
|
output *outputOptimization
|
||||||
saveTarget Output
|
saveTarget Output
|
||||||
snapshotArchive *editorSnapshotArchive
|
snapshotArchive *editorSnapshotArchive
|
||||||
validatedPaths *renderCache[string, bool]
|
validatedPaths *renderCache[string, bool]
|
||||||
richIndex *richTextIndex
|
changes EditorChanges
|
||||||
|
transactionChanges *EditorChanges
|
||||||
|
objectIndex *editorObjectIndex
|
||||||
|
richIndex *richTextIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
// editorResource 文档内嵌资源
|
// editorResource 文档内嵌资源
|
||||||
@@ -892,13 +895,8 @@ func (e *Editor) findObject(page int, id string) (*Layer, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
for i := range content.Content.Layer {
|
if position, ok := e.objectPositions(content)[id]; ok {
|
||||||
layer := &content.Content.Layer[i]
|
return &content.Content.Layer[position.layer], position.index, nil
|
||||||
for index, object := range layer.Objects {
|
|
||||||
if editorObjectID(object) == id && id != "" {
|
|
||||||
return layer, index, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil, 0, fmt.Errorf("object %q not found on page %d", id, page)
|
return nil, 0, fmt.Errorf("object %q not found on page %d", id, page)
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-22
@@ -30,6 +30,36 @@ type editorObjectPosition struct {
|
|||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editorObjectIndex 复用最近页面的只读对象位置,图层容器替换后重新建立
|
||||||
|
type editorObjectIndex struct {
|
||||||
|
layers []Layer
|
||||||
|
positions map[string]editorObjectPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
// objectPositions 获取页面的对象位置索引,重复编号保留第一个匹配
|
||||||
|
// 入参: page 内部只读页面
|
||||||
|
// 返回: map[string]editorObjectPosition 只读索引
|
||||||
|
func (e *Editor) objectPositions(page *PageContent) map[string]editorObjectPosition {
|
||||||
|
layers := page.Content.Layer
|
||||||
|
if len(layers) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if cached := e.objectIndex; cached != nil && len(cached.layers) == len(layers) && &cached.layers[0] == &layers[0] {
|
||||||
|
return cached.positions
|
||||||
|
}
|
||||||
|
positions := make(map[string]editorObjectPosition)
|
||||||
|
for i, layer := range layers {
|
||||||
|
for j, object := range layer.Objects {
|
||||||
|
id := editorObjectID(object)
|
||||||
|
if _, exists := positions[id]; id != "" && !exists {
|
||||||
|
positions[id] = editorObjectPosition{i, j}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.objectIndex = &editorObjectIndex{layers: layers, positions: positions}
|
||||||
|
return positions
|
||||||
|
}
|
||||||
|
|
||||||
// ObjectPosition 对象在图层或页块直接成员中的位置,Index从0开始,Count不含嵌套页块的对象
|
// ObjectPosition 对象在图层或页块直接成员中的位置,Index从0开始,Count不含嵌套页块的对象
|
||||||
type ObjectPosition struct {
|
type ObjectPosition struct {
|
||||||
Layer string
|
Layer string
|
||||||
@@ -709,7 +739,7 @@ func (e *Editor) replaceLayers(page int, layers []Layer) {
|
|||||||
func (e *Editor) replacePreparedLayers(page int, after []Layer) {
|
func (e *Editor) replacePreparedLayers(page int, after []Layer) {
|
||||||
if e.historyLimit == 0 {
|
if e.historyLimit == 0 {
|
||||||
e.pages[page].Content.Layer = after
|
e.pages[page].Content.Layer = after
|
||||||
e.recordChange()
|
e.recordChange(page)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
before := copyEditorPage(e.pages[page])
|
before := copyEditorPage(e.pages[page])
|
||||||
@@ -738,7 +768,7 @@ func (e *Editor) replacePreparedLayers(page int, after []Layer) {
|
|||||||
e.pages[page].Content.Layer = copyEditorPage(PageContent{Content: Content{Layer: layers}}).Content.Layer
|
e.pages[page].Content.Layer = copyEditorPage(PageContent{Content: Content{Layer: layers}}).Content.Layer
|
||||||
}
|
}
|
||||||
apply(e, after)
|
apply(e, after)
|
||||||
if change := e.recordChange(); change != nil {
|
if change := e.recordChange(page); change != nil {
|
||||||
change.undo = func(e *Editor) { apply(e, before.Content.Layer) }
|
change.undo = func(e *Editor) { apply(e, before.Content.Layer) }
|
||||||
change.redo = func(e *Editor) { apply(e, after) }
|
change.redo = func(e *Editor) { apply(e, after) }
|
||||||
}
|
}
|
||||||
@@ -835,6 +865,15 @@ func (e *Editor) updateObjectOrigins(page int, objects []GraphicObject, preserve
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
apply := func(e *Editor, objects []GraphicObject, origins map[string]*editorObjectOrigin) {
|
apply := func(e *Editor, objects []GraphicObject, origins map[string]*editorObjectOrigin) {
|
||||||
|
layers := slices.Clone(e.pages[page].Content.Layer)
|
||||||
|
cloned := make(map[int]bool)
|
||||||
|
for _, index := range indexes {
|
||||||
|
if !cloned[index.layer] {
|
||||||
|
layers[index.layer].Objects = slices.Clone(layers[index.layer].Objects)
|
||||||
|
cloned[index.layer] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.pages[page].Content.Layer = layers
|
||||||
for i, index := range indexes {
|
for i, index := range indexes {
|
||||||
e.pages[page].Content.Layer[index.layer].Objects[index.index] = objects[i]
|
e.pages[page].Content.Layer[index.layer].Objects[index.index] = objects[i]
|
||||||
}
|
}
|
||||||
@@ -843,7 +882,7 @@ func (e *Editor) updateObjectOrigins(page int, objects []GraphicObject, preserve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply(e, after, afterOrigins)
|
apply(e, after, afterOrigins)
|
||||||
if change := e.recordChange(); change != nil {
|
if change := e.recordChange(page); change != nil {
|
||||||
change.undo = func(e *Editor) { apply(e, before, beforeOrigins) }
|
change.undo = func(e *Editor) { apply(e, before, beforeOrigins) }
|
||||||
change.redo = func(e *Editor) { apply(e, after, afterOrigins) }
|
change.redo = func(e *Editor) { apply(e, after, afterOrigins) }
|
||||||
}
|
}
|
||||||
@@ -891,30 +930,19 @@ func (e *Editor) selectedObjects(page int, ids []string) ([]GraphicObject, []edi
|
|||||||
if len(ids) == 0 {
|
if len(ids) == 0 {
|
||||||
return objects, indexes, nil
|
return objects, indexes, nil
|
||||||
}
|
}
|
||||||
selected := make(map[string]int, len(ids))
|
positions := e.objectPositions(content)
|
||||||
|
selected := make(map[string]bool, len(ids))
|
||||||
for i, id := range ids {
|
for i, id := range ids {
|
||||||
if _, exists := selected[id]; exists {
|
if selected[id] {
|
||||||
return nil, nil, fmt.Errorf("duplicate object %q", id)
|
return nil, nil, fmt.Errorf("duplicate object %q", id)
|
||||||
}
|
}
|
||||||
selected[id] = i
|
selected[id] = true
|
||||||
}
|
position, ok := positions[id]
|
||||||
for i, layer := range content.Content.Layer {
|
if !ok {
|
||||||
for j, object := range layer.Objects {
|
|
||||||
id := editorObjectID(object)
|
|
||||||
if index, ok := selected[id]; ok && id != "" {
|
|
||||||
objects[index] = object
|
|
||||||
indexes[index] = editorObjectPosition{i, j}
|
|
||||||
delete(selected, id)
|
|
||||||
if len(selected) == 0 {
|
|
||||||
return objects, indexes, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, id := range ids {
|
|
||||||
if _, missing := selected[id]; missing {
|
|
||||||
return nil, nil, fmt.Errorf("object %q not found on page %d", id, page)
|
return nil, nil, fmt.Errorf("object %q not found on page %d", id, page)
|
||||||
}
|
}
|
||||||
|
indexes[i] = position
|
||||||
|
objects[i] = content.Content.Layer[position.layer].Objects[position.index]
|
||||||
}
|
}
|
||||||
return objects, indexes, nil
|
return objects, indexes, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
@@ -241,6 +242,22 @@ func (e *Editor) copiedLayerStyle(object GraphicObject) string {
|
|||||||
// 入参: value 原值
|
// 入参: value 原值
|
||||||
// 返回: T 独立副本
|
// 返回: T 独立副本
|
||||||
func cloneEditorData[T any](value T) T {
|
func cloneEditorData[T any](value T) T {
|
||||||
|
switch source := any(value).(type) {
|
||||||
|
case GraphicObject:
|
||||||
|
return any(copyEditorGraphicObject(source)).(T)
|
||||||
|
case TextObject:
|
||||||
|
return any(copyEditorTextObject(source)).(T)
|
||||||
|
case PathObject:
|
||||||
|
return any(copyEditorPathObject(source)).(T)
|
||||||
|
case ImageObject:
|
||||||
|
return any(copyEditorImageObject(source)).(T)
|
||||||
|
case CompositeGraphicUnit:
|
||||||
|
return any(copyEditorCompositeGraphicUnit(source)).(T)
|
||||||
|
case PageContent:
|
||||||
|
return any(copyEditorPageContent(source)).(T)
|
||||||
|
case Layer:
|
||||||
|
return any(copyEditorLayer(source)).(T)
|
||||||
|
}
|
||||||
source := reflect.ValueOf(value)
|
source := reflect.ValueOf(value)
|
||||||
if !source.IsValid() {
|
if !source.IsValid() {
|
||||||
return value
|
return value
|
||||||
@@ -336,3 +353,174 @@ func editorCloneFields(kind reflect.Type) []int {
|
|||||||
actual, _ := editorClonePlans.LoadOrStore(kind, fields)
|
actual, _ := editorClonePlans.LoadOrStore(kind, fields)
|
||||||
return actual.([]int)
|
return actual.([]int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copyEditorGraphicObject 复制GraphicObject的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: GraphicObject 独立副本
|
||||||
|
func copyEditorGraphicObject(value GraphicObject) GraphicObject {
|
||||||
|
value.TextObject = copyEditorTextObject(value.TextObject)
|
||||||
|
value.PathObject = copyEditorPathObject(value.PathObject)
|
||||||
|
value.ImageObject = copyEditorImageObject(value.ImageObject)
|
||||||
|
value.CompositeGraphicUnit = copyEditorCompositeGraphicUnit(value.CompositeGraphicUnit)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyEditorTextObject 复制TextObject的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: TextObject 独立副本
|
||||||
|
func copyEditorTextObject(value TextObject) TextObject {
|
||||||
|
if value.DashOffset != nil {
|
||||||
|
copied := *value.DashOffset
|
||||||
|
value.DashOffset = &copied
|
||||||
|
}
|
||||||
|
if value.Alpha != nil {
|
||||||
|
copied := *value.Alpha
|
||||||
|
value.Alpha = &copied
|
||||||
|
}
|
||||||
|
if value.Visible != nil {
|
||||||
|
copied := *value.Visible
|
||||||
|
value.Visible = &copied
|
||||||
|
}
|
||||||
|
if value.Fill != nil {
|
||||||
|
copied := *value.Fill
|
||||||
|
value.Fill = &copied
|
||||||
|
}
|
||||||
|
if value.Stroke != nil {
|
||||||
|
copied := *value.Stroke
|
||||||
|
value.Stroke = &copied
|
||||||
|
}
|
||||||
|
value.StrokeColor = cloneEditorData(value.StrokeColor)
|
||||||
|
value.FillColor = cloneEditorData(value.FillColor)
|
||||||
|
value.CGTransform = slices.Clone(value.CGTransform)
|
||||||
|
value.TextCode = slices.Clone(value.TextCode)
|
||||||
|
value.Clips = cloneEditorData(value.Clips)
|
||||||
|
value.Actions = cloneEditorData(value.Actions)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyEditorPathObject 复制PathObject的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: PathObject 独立副本
|
||||||
|
func copyEditorPathObject(value PathObject) PathObject {
|
||||||
|
if value.DashOffset != nil {
|
||||||
|
copied := *value.DashOffset
|
||||||
|
value.DashOffset = &copied
|
||||||
|
}
|
||||||
|
if value.Alpha != nil {
|
||||||
|
copied := *value.Alpha
|
||||||
|
value.Alpha = &copied
|
||||||
|
}
|
||||||
|
if value.Visible != nil {
|
||||||
|
copied := *value.Visible
|
||||||
|
value.Visible = &copied
|
||||||
|
}
|
||||||
|
if value.Stroke != nil {
|
||||||
|
copied := *value.Stroke
|
||||||
|
value.Stroke = &copied
|
||||||
|
}
|
||||||
|
if value.Fill != nil {
|
||||||
|
copied := *value.Fill
|
||||||
|
value.Fill = &copied
|
||||||
|
}
|
||||||
|
value.StrokeColor = cloneEditorData(value.StrokeColor)
|
||||||
|
value.FillColor = cloneEditorData(value.FillColor)
|
||||||
|
value.Clips = cloneEditorData(value.Clips)
|
||||||
|
value.Actions = cloneEditorData(value.Actions)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyEditorImageObject 复制ImageObject的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: ImageObject 独立副本
|
||||||
|
func copyEditorImageObject(value ImageObject) ImageObject {
|
||||||
|
if value.Alpha != nil {
|
||||||
|
copied := *value.Alpha
|
||||||
|
value.Alpha = &copied
|
||||||
|
}
|
||||||
|
if value.Visible != nil {
|
||||||
|
copied := *value.Visible
|
||||||
|
value.Visible = &copied
|
||||||
|
}
|
||||||
|
value.Border = cloneEditorData(value.Border)
|
||||||
|
value.Clips = cloneEditorData(value.Clips)
|
||||||
|
value.Actions = cloneEditorData(value.Actions)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyEditorCompositeGraphicUnit 复制CompositeGraphicUnit的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: CompositeGraphicUnit 独立副本
|
||||||
|
func copyEditorCompositeGraphicUnit(value CompositeGraphicUnit) CompositeGraphicUnit {
|
||||||
|
if value.Alpha != nil {
|
||||||
|
copied := *value.Alpha
|
||||||
|
value.Alpha = &copied
|
||||||
|
}
|
||||||
|
if value.Visible != nil {
|
||||||
|
copied := *value.Visible
|
||||||
|
value.Visible = &copied
|
||||||
|
}
|
||||||
|
value.Objects = slices.Clone(value.Objects)
|
||||||
|
for i := range value.Objects {
|
||||||
|
value.Objects[i] = copyEditorGraphicObject(value.Objects[i])
|
||||||
|
}
|
||||||
|
value.TextObject = slices.Clone(value.TextObject)
|
||||||
|
for i := range value.TextObject {
|
||||||
|
value.TextObject[i] = copyEditorTextObject(value.TextObject[i])
|
||||||
|
}
|
||||||
|
value.PathObject = slices.Clone(value.PathObject)
|
||||||
|
for i := range value.PathObject {
|
||||||
|
value.PathObject[i] = copyEditorPathObject(value.PathObject[i])
|
||||||
|
}
|
||||||
|
value.ImageObject = slices.Clone(value.ImageObject)
|
||||||
|
for i := range value.ImageObject {
|
||||||
|
value.ImageObject[i] = copyEditorImageObject(value.ImageObject[i])
|
||||||
|
}
|
||||||
|
value.CompositeGraphicUnit = slices.Clone(value.CompositeGraphicUnit)
|
||||||
|
for i := range value.CompositeGraphicUnit {
|
||||||
|
value.CompositeGraphicUnit[i] = copyEditorCompositeGraphicUnit(value.CompositeGraphicUnit[i])
|
||||||
|
}
|
||||||
|
value.Clips = cloneEditorData(value.Clips)
|
||||||
|
value.Actions = cloneEditorData(value.Actions)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyEditorPageContent 复制PageContent的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: PageContent 独立副本
|
||||||
|
func copyEditorPageContent(value PageContent) PageContent {
|
||||||
|
value.Template = slices.Clone(value.Template)
|
||||||
|
value.PageRes = slices.Clone(value.PageRes)
|
||||||
|
value.Content.Layer = slices.Clone(value.Content.Layer)
|
||||||
|
for i := range value.Content.Layer {
|
||||||
|
value.Content.Layer[i] = copyEditorLayer(value.Content.Layer[i])
|
||||||
|
}
|
||||||
|
value.Actions = cloneEditorData(value.Actions)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyEditorLayer 复制Layer的可变字段,保留内部只读状态
|
||||||
|
// 入参: value 原值
|
||||||
|
// 返回: Layer 独立副本
|
||||||
|
func copyEditorLayer(value Layer) Layer {
|
||||||
|
value.Objects = slices.Clone(value.Objects)
|
||||||
|
for i := range value.Objects {
|
||||||
|
value.Objects[i] = copyEditorGraphicObject(value.Objects[i])
|
||||||
|
}
|
||||||
|
value.TextObject = slices.Clone(value.TextObject)
|
||||||
|
for i := range value.TextObject {
|
||||||
|
value.TextObject[i] = copyEditorTextObject(value.TextObject[i])
|
||||||
|
}
|
||||||
|
value.PathObject = slices.Clone(value.PathObject)
|
||||||
|
for i := range value.PathObject {
|
||||||
|
value.PathObject[i] = copyEditorPathObject(value.PathObject[i])
|
||||||
|
}
|
||||||
|
value.ImageObject = slices.Clone(value.ImageObject)
|
||||||
|
for i := range value.ImageObject {
|
||||||
|
value.ImageObject[i] = copyEditorImageObject(value.ImageObject[i])
|
||||||
|
}
|
||||||
|
value.CompositeGraphicUnit = slices.Clone(value.CompositeGraphicUnit)
|
||||||
|
for i := range value.CompositeGraphicUnit {
|
||||||
|
value.CompositeGraphicUnit[i] = copyEditorCompositeGraphicUnit(value.CompositeGraphicUnit[i])
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
+23
-13
@@ -34,8 +34,9 @@ type editorFontUsage struct {
|
|||||||
unsafe bool
|
unsafe bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// editorFontSubset 保存单个新增字体最近一次裁剪结果,不保留历史字形集合
|
// editorFontSubset 保存单个新增字体及其快照最近一次裁剪结果,不保留历史字形集合
|
||||||
type editorFontSubset struct {
|
type editorFontSubset struct {
|
||||||
|
source [32]byte
|
||||||
glyphs []uint16
|
glyphs []uint16
|
||||||
data []byte
|
data []byte
|
||||||
mapped bool
|
mapped bool
|
||||||
@@ -317,13 +318,13 @@ func (e *Editor) subsetSnapshotFonts(ctx context.Context, progress editorProgres
|
|||||||
if e.source == nil || e.backends.FontResources == nil {
|
if e.source == nil || e.backends.FontResources == nil {
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
}
|
}
|
||||||
candidates := make(map[string]bool)
|
candidates := make(map[string]int)
|
||||||
for _, resource := range e.resources {
|
for i, resource := range e.resources {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if resource.font != nil && resource.font.FontFile != "" && e.source.reader.fileNames[resource.name] {
|
if resource.font != nil && resource.font.FontFile != "" && e.source.reader.fileNames[resource.name] {
|
||||||
candidates[resource.name] = true
|
candidates[resource.name] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(candidates) == 0 {
|
if len(candidates) == 0 {
|
||||||
@@ -399,15 +400,24 @@ func (e *Editor) subsetSnapshotFonts(ctx context.Context, progress editorProgres
|
|||||||
if font.unsafe {
|
if font.unsafe {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
subset, err := e.backends.FontResources.SubsetFont(data, slices.Sorted(maps.Keys(glyphs)), preserve)
|
ids := slices.Sorted(maps.Keys(glyphs))
|
||||||
if err != nil {
|
checksum := sha256.Sum256(data)
|
||||||
return nil, err
|
resource := &e.resources[candidates[name]]
|
||||||
|
if resource.subset == nil || resource.subset.source != checksum || resource.subset.mapped != preserve || !slices.Equal(resource.subset.glyphs, ids) {
|
||||||
|
subset, err := e.backends.FontResources.SubsetFont(data, ids, preserve)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(subset) >= len(data) {
|
||||||
|
subset = nil
|
||||||
|
}
|
||||||
|
resource.subset = &editorFontSubset{source: checksum, glyphs: ids, data: subset, mapped: preserve}
|
||||||
}
|
}
|
||||||
if err := ctx.Err(); err != nil {
|
if len(resource.subset.data) != 0 {
|
||||||
return nil, err
|
result[name] = resource.subset.data
|
||||||
}
|
|
||||||
if len(subset) != 0 && len(subset) < len(data) {
|
|
||||||
result[name] = subset
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result, progress.report("fonts", len(candidates), len(candidates))
|
return result, progress.report("fonts", len(candidates), len(candidates))
|
||||||
@@ -593,7 +603,7 @@ func (e *Editor) subsetNewFonts(progress editorProgress) (map[string][]byte, err
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resource := &e.resources[i]
|
resource := &e.resources[i]
|
||||||
if resource.font == nil {
|
if resource.font == nil || e.source != nil && e.source.reader.fileNames[resource.name] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
usage := used[resource.font.ID]
|
usage := used[resource.font.ID]
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ func (e *Editor) replaceGroup(page int, indexes []editorObjectPosition, objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply(e, after, origins)
|
apply(e, after, origins)
|
||||||
if change := e.recordChange(); change != nil {
|
if change := e.recordChange(page); change != nil {
|
||||||
change.undo = func(e *Editor) { apply(e, before, previous) }
|
change.undo = func(e *Editor) { apply(e, before, previous) }
|
||||||
change.redo = func(e *Editor) { apply(e, after, origins) }
|
change.redo = func(e *Editor) { apply(e, after, origins) }
|
||||||
}
|
}
|
||||||
|
|||||||
+71
-15
@@ -21,10 +21,43 @@ import (
|
|||||||
|
|
||||||
// editorChange 页面或对象操作及其修订标识
|
// editorChange 页面或对象操作及其修订标识
|
||||||
type editorChange struct {
|
type editorChange struct {
|
||||||
undo func(*Editor)
|
undo func(*Editor)
|
||||||
redo func(*Editor)
|
redo func(*Editor)
|
||||||
before uint64
|
before uint64
|
||||||
after uint64
|
after uint64
|
||||||
|
changes EditorChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditorChanges 描述最近一次修改或历史回放的刷新范围
|
||||||
|
// All表示文档结构或共享资源变化;否则Pages为受影响的零基页码
|
||||||
|
// 返回值只覆盖最近一次操作,调用方应在每次操作后读取
|
||||||
|
type EditorChanges struct {
|
||||||
|
All bool `json:"all"`
|
||||||
|
Pages []int `json:"pages,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes 获取最近一次有效修改的独立刷新范围
|
||||||
|
// 返回: EditorChanges 变化范围;未发生修改时为空
|
||||||
|
func (e *Editor) Changes() EditorChanges {
|
||||||
|
return EditorChanges{All: e.changes.All, Pages: slices.Clone(e.changes.Pages)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge 合并同一事务中的变化范围
|
||||||
|
// 入参: change 待合并的变化
|
||||||
|
func (c *EditorChanges) merge(change EditorChanges) {
|
||||||
|
if c.All {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if change.All {
|
||||||
|
c.All = true
|
||||||
|
c.Pages = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, page := range change.Pages {
|
||||||
|
if !slices.Contains(c.Pages, page) {
|
||||||
|
c.Pages = append(c.Pages, page)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transaction 将多项修改作为一次原子操作提交,失败时保留原文档及历史
|
// Transaction 将多项修改作为一次原子操作提交,失败时保留原文档及历史
|
||||||
@@ -37,6 +70,8 @@ func (e *Editor) Transaction(edit func(*Editor) error) error {
|
|||||||
before.Info = cloneEditorData(e.Info)
|
before.Info = cloneEditorData(e.Info)
|
||||||
next := e.transactionSnapshot()
|
next := e.transactionSnapshot()
|
||||||
next.history, next.historyIndex, next.historyLimit = nil, 0, 0
|
next.history, next.historyIndex, next.historyLimit = nil, 0, 0
|
||||||
|
pending := &EditorChanges{}
|
||||||
|
next.transactionChanges = pending
|
||||||
*e = next
|
*e = next
|
||||||
committed := false
|
committed := false
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -48,6 +83,11 @@ func (e *Editor) Transaction(edit func(*Editor) error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
changed := e.revision != before.revision
|
changed := e.revision != before.revision
|
||||||
|
changes := *pending
|
||||||
|
if e.transactionChanges != pending {
|
||||||
|
changes = EditorChanges{All: true}
|
||||||
|
}
|
||||||
|
e.transactionChanges = before.transactionChanges
|
||||||
e.history, e.historyIndex, e.historyLimit = before.history, before.historyIndex, before.historyLimit
|
e.history, e.historyIndex, e.historyLimit = before.history, before.historyIndex, before.historyLimit
|
||||||
if !sameBackend(e.backends.FontResources, before.backends.FontResources) {
|
if !sameBackend(e.backends.FontResources, before.backends.FontResources) {
|
||||||
e.clearFontResourceCache()
|
e.clearFontResourceCache()
|
||||||
@@ -62,7 +102,7 @@ func (e *Editor) Transaction(edit func(*Editor) error) error {
|
|||||||
}
|
}
|
||||||
if changed {
|
if changed {
|
||||||
e.serial, e.revision = before.serial, before.revision
|
e.serial, e.revision = before.serial, before.revision
|
||||||
e.recordTransaction(before)
|
e.recordTransaction(before, changes)
|
||||||
}
|
}
|
||||||
committed = true
|
committed = true
|
||||||
return nil
|
return nil
|
||||||
@@ -108,6 +148,7 @@ func (e *Editor) Undo() bool {
|
|||||||
e.historyIndex--
|
e.historyIndex--
|
||||||
change := e.history[e.historyIndex]
|
change := e.history[e.historyIndex]
|
||||||
change.undo(e)
|
change.undo(e)
|
||||||
|
e.changes, e.objectIndex = change.changes, nil
|
||||||
e.revision = change.before
|
e.revision = change.before
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -120,6 +161,7 @@ func (e *Editor) Redo() bool {
|
|||||||
}
|
}
|
||||||
change := e.history[e.historyIndex]
|
change := e.history[e.historyIndex]
|
||||||
change.redo(e)
|
change.redo(e)
|
||||||
|
e.changes, e.objectIndex = change.changes, nil
|
||||||
e.historyIndex++
|
e.historyIndex++
|
||||||
e.revision = change.after
|
e.revision = change.after
|
||||||
return true
|
return true
|
||||||
@@ -132,15 +174,12 @@ func (e *Editor) Revision() uint64 {
|
|||||||
return e.revision
|
return e.revision
|
||||||
}
|
}
|
||||||
|
|
||||||
// transactionSnapshot 复制可变容器,保留只读XML、图元及二进制资源的共享
|
// transactionSnapshot 复制顶层容器,共享按替换方式更新的页面图层、图元和二进制资源
|
||||||
// 返回: Editor 独立事务状态
|
// 返回: Editor 独立事务状态
|
||||||
func (e *Editor) transactionSnapshot() Editor {
|
func (e *Editor) transactionSnapshot() Editor {
|
||||||
next := *e
|
next := *e
|
||||||
next.Info = cloneEditorData(e.Info)
|
next.Info = cloneEditorData(e.Info)
|
||||||
next.pages = make([]PageContent, len(e.pages))
|
next.pages = slices.Clone(e.pages)
|
||||||
for i, page := range e.pages {
|
|
||||||
next.pages[i] = copyEditorPage(page)
|
|
||||||
}
|
|
||||||
next.resources = slices.Clone(e.resources)
|
next.resources = slices.Clone(e.resources)
|
||||||
next.fonts, next.images, next.resourceID = maps.Clone(e.fonts), maps.Clone(e.images), maps.Clone(e.resourceID)
|
next.fonts, next.images, next.resourceID = maps.Clone(e.fonts), maps.Clone(e.images), maps.Clone(e.resourceID)
|
||||||
next.origins = maps.Clone(e.origins)
|
next.origins = maps.Clone(e.origins)
|
||||||
@@ -213,9 +252,13 @@ func (e *Editor) retainDocumentResources(source *Editor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// recordTransaction 记录事务快照,在各自资源视图中保留后续注册的资源
|
// recordTransaction 记录事务快照,在各自资源视图中保留后续注册的资源
|
||||||
// 入参: before 修改前的文档状态
|
// 入参: before 修改前的文档状态, changes 可选的事务变化范围
|
||||||
func (e *Editor) recordTransaction(before Editor) {
|
func (e *Editor) recordTransaction(before Editor, changes ...EditorChanges) {
|
||||||
if change := e.recordChange(); change != nil {
|
scope := EditorChanges{All: true}
|
||||||
|
if len(changes) != 0 {
|
||||||
|
scope = changes[0]
|
||||||
|
}
|
||||||
|
if change := e.recordChangeScope(scope); change != nil {
|
||||||
after := e.transactionSnapshot()
|
after := e.transactionSnapshot()
|
||||||
before.history, after.history = nil, nil
|
before.history, after.history = nil, nil
|
||||||
change.undo = func(e *Editor) {
|
change.undo = func(e *Editor) {
|
||||||
@@ -230,10 +273,23 @@ func (e *Editor) recordTransaction(before Editor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// recordChange 分配修订标识并为已完成的有效修改预留记录
|
// recordChange 分配修订标识并为已完成的有效修改预留记录
|
||||||
|
// 入参: pages 受影响页码,省略表示全局变化
|
||||||
// 返回: *editorChange 记录,未启用历史时为nil
|
// 返回: *editorChange 记录,未启用历史时为nil
|
||||||
func (e *Editor) recordChange() *editorChange {
|
func (e *Editor) recordChange(pages ...int) *editorChange {
|
||||||
|
return e.recordChangeScope(EditorChanges{All: len(pages) == 0, Pages: slices.Clone(pages)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// recordChangeScope 分配修订并记录变更范围,事务中同步合并
|
||||||
|
// 入参: scope 变更范围
|
||||||
|
// 返回: *editorChange 可补充回放操作的记录
|
||||||
|
func (e *Editor) recordChangeScope(scope EditorChanges) *editorChange {
|
||||||
|
e.objectIndex = nil
|
||||||
|
e.changes = scope
|
||||||
|
if e.transactionChanges != nil {
|
||||||
|
e.transactionChanges.merge(scope)
|
||||||
|
}
|
||||||
e.serial++
|
e.serial++
|
||||||
change := editorChange{before: e.revision, after: e.serial}
|
change := editorChange{before: e.revision, after: e.serial, changes: scope}
|
||||||
e.revision = e.serial
|
e.revision = e.serial
|
||||||
if e.historyLimit == 0 {
|
if e.historyLimit == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -791,6 +791,14 @@ func (e *Editor) writePartsWithPages(ctx context.Context, write func(string, []b
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// editorResourceSelection 保存单次输出中同一编辑快照的资源筛选结果
|
||||||
|
type editorResourceSelection struct {
|
||||||
|
owner *Editor
|
||||||
|
fonts, images []editorResource
|
||||||
|
spaces []ColorSpace
|
||||||
|
files map[string]bool
|
||||||
|
}
|
||||||
|
|
||||||
// usedResources 沿图层、对象及资源依赖筛选新增资源和共享文件,不修改资源池
|
// usedResources 沿图层、对象及资源依赖筛选新增资源和共享文件,不修改资源池
|
||||||
// 入参: ctx 取消上下文
|
// 入参: ctx 取消上下文
|
||||||
// 返回: []editorResource 新增字体, []editorResource 新增图片, []ColorSpace 新增颜色空间, map[string]bool 独立资源文件集合, error 取消错误
|
// 返回: []editorResource 新增字体, []editorResource 新增图片, []ColorSpace 新增颜色空间, map[string]bool 独立资源文件集合, error 取消错误
|
||||||
@@ -798,6 +806,10 @@ func (e *Editor) usedResources(ctx context.Context) (fonts, images []editorResou
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
if e.output != nil && e.output.resources != nil && e.output.resources.owner == e {
|
||||||
|
cached := e.output.resources
|
||||||
|
return slices.Clone(cached.fonts), slices.Clone(cached.images), slices.Clone(cached.spaces), maps.Clone(cached.files), nil
|
||||||
|
}
|
||||||
used := make(map[string]bool)
|
used := make(map[string]bool)
|
||||||
promoted := make(map[string]bool)
|
promoted := make(map[string]bool)
|
||||||
files := make(map[string]bool)
|
files := make(map[string]bool)
|
||||||
@@ -945,6 +957,9 @@ func (e *Editor) usedResources(ctx context.Context) (fonts, images []editorResou
|
|||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
if e.output != nil {
|
||||||
|
e.output.resources = &editorResourceSelection{owner: e, fonts: slices.Clone(fonts), images: slices.Clone(images), spaces: slices.Clone(spaces), files: maps.Clone(files)}
|
||||||
|
}
|
||||||
return fonts, images, spaces, files, nil
|
return fonts, images, spaces, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user