mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 08:46:55 +08:00
This commit is contained in:
1 parent
c2d9a349c5
commit
0f2fb7a842
17 files changed
+105
-53
No files matched your search
@@ -57,7 +57,7 @@
|
||||
<option value="300" selected>300 dpi</option>
|
||||
<option value="600">600 dpi</option>
|
||||
</select><span id="dpiValue" aria-hidden="true">300</span></label>
|
||||
<label class="toolbar-select"><select id="outputCompression" class="format-select" title="压缩模式" aria-label="压缩模式" disabled><option value="0">默认</option><option value="1">无损</option><option value="2">轻压</option><option value="3">均衡</option><option value="4">强压</option></select><span id="compressionValue" data-short="默" aria-hidden="true">默认</span></label>
|
||||
<label class="toolbar-select"><select id="outputCompression" class="format-select" title="压缩模式" aria-label="压缩模式" disabled><option value="0">原始</option><option value="1">无损</option><option value="2">轻压</option><option value="3">均衡</option><option value="4">强压</option></select><span id="compressionValue" data-short="原" aria-hidden="true">原始</span></label>
|
||||
<button id="exportPageButton" class="button" type="button" data-short="单" title="导出单页" aria-label="导出单页" disabled>单页</button>
|
||||
<button id="exportButton" class="button" type="button" data-short="多" title="导出文档" aria-label="导出文档" aria-haspopup="dialog" aria-controls="exportPanel" disabled>文档</button>
|
||||
</div>
|
||||
@@ -184,7 +184,7 @@
|
||||
<button id="convertAdd" class="button" type="button">添加</button><button id="convertClear" class="button" type="button">清空</button>
|
||||
<div class="convert-options">
|
||||
<div class="form-row"><label for="convertFormat">格式</label><select id="convertFormat" required></select></div>
|
||||
<div class="form-row"><label for="convertCompression">压缩</label><select id="convertCompression"><option value="0">默认</option><option value="1">无损</option><option value="2">轻压</option><option value="3">均衡</option><option value="4">强压</option></select></div>
|
||||
<div class="form-row"><label for="convertCompression">压缩</label><select id="convertCompression"><option value="0">原始</option><option value="1">无损</option><option value="2">轻压</option><option value="3">均衡</option><option value="4">强压</option></select></div>
|
||||
<div id="convertFontRow" class="form-row" hidden><label for="convertFontMode">字体</label><select id="convertFontMode"><option value="0">保持</option><option value="1">内嵌</option><option value="2">外部</option></select></div>
|
||||
<div id="convertDPIRow" class="form-row" hidden><label for="convertDPI">精度</label><select id="convertDPI"><option value="96">96 dpi</option><option value="150">150 dpi</option><option value="300" selected>300 dpi</option><option value="600">600 dpi</option></select></div>
|
||||
<div class="form-row"><label for="convertDestination">保存</label><select id="convertDestination"><option value="directory">目录</option><option value="archive">打包</option></select></div>
|
||||
|
||||
@@ -1381,7 +1381,7 @@ document.addEventListener("selectionchange", syncSelection);
|
||||
el.exportFormat.addEventListener("change", () => updateDPIControl());
|
||||
el.outputCompression.addEventListener("change", () => {
|
||||
el.compressionValue.textContent = el.outputCompression.selectedOptions[0].textContent;
|
||||
el.compressionValue.dataset.short = ["默", "无", "轻", "均", "强"][Number(el.outputCompression.value)];
|
||||
el.compressionValue.dataset.short = ["原", "无", "轻", "均", "强"][Number(el.outputCompression.value)];
|
||||
});
|
||||
el.exportPageButton.addEventListener("click", () => exportFile(false));
|
||||
el.exportButton.addEventListener("click", () => openExportPanel());
|
||||
|
||||
@@ -10,7 +10,7 @@ require (
|
||||
github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6
|
||||
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893
|
||||
github.com/xiaoqidun/jbig2 v1.1.4
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261010014421-57af90cea2c6
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261010042215-c8c66c83f031
|
||||
golang.org/x/crypto v0.58.0
|
||||
golang.org/x/image v0.47.0
|
||||
golang.org/x/text v0.43.0
|
||||
|
||||
@@ -62,8 +62,8 @@ github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893 h1:jmQlBO7R+bcimOM
|
||||
github.com/xiaoqidun/j2kgo v0.0.0-20261009063910-4865a21ef893/go.mod h1:DCwAfuDzCDYqUiAlZ7BDwigTjaXw7o9L1FtEma/D1aA=
|
||||
github.com/xiaoqidun/jbig2 v1.1.4 h1:LF6J1si4JY+TszTh9+m4KAg9/ba0JjfXoGtQTQC2S8c=
|
||||
github.com/xiaoqidun/jbig2 v1.1.4/go.mod h1:9jromqVwMqlptUDEt6Yip8vqYA5I1YjhG7FgMl/Z2dA=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261010014421-57af90cea2c6 h1:APuDHlsRUfDMkV7+ONj2FwRFRGkO+KzbHqBoWh2aG54=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261010014421-57af90cea2c6/go.mod h1:fXz1emtF1YCpcIv+fglDZstjkHxjS2l3G4DVMgUzVnk=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261010042215-c8c66c83f031 h1:FV9MPGAQ08+AGYMPKesJWl42eLL5TPU+4niOlY1sVTY=
|
||||
github.com/xiaoqidun/pdfgo v0.0.0-20261010042215-c8c66c83f031/go.mod h1:fXz1emtF1YCpcIv+fglDZstjkHxjS2l3G4DVMgUzVnk=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA=
|
||||
|
||||
@@ -46,13 +46,13 @@ const (
|
||||
// outputOptimizationBufferLimit 限制压缩时的单个输出缓冲,单位为字节
|
||||
const outputOptimizationBufferLimit = 64 << 20
|
||||
|
||||
// CompressionMode 选择默认、无损或有损输出策略
|
||||
// CompressionMode 选择原始、无损或有损输出策略
|
||||
type CompressionMode = pdfgo.CompressionMode
|
||||
|
||||
// CompressionLevel 选择轻压、均衡或强压,仅用于有损模式
|
||||
type CompressionLevel = pdfgo.CompressionLevel
|
||||
|
||||
// CompressionOptions 配置输出压缩,默认模式沿用现有输出策略,不额外优化
|
||||
// CompressionOptions 配置输出压缩,原始模式沿用现有输出策略,不额外优化
|
||||
// Quality和MaxDPI为零时按Level使用预设,不改变页面尺寸或整页导出精度
|
||||
// 无损表示不引入额外损失,不保证跨格式转换可逆;有损不栅格化文字和矢量
|
||||
type CompressionOptions = pdfgo.CompressionOptions
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ func OutputFormats() []OutputFormat {
|
||||
}
|
||||
|
||||
// Convert 将PDF或OFD转换为指定格式,不关闭调用方的输入和输出
|
||||
// 默认模式完整OFD另存保持源字节;选页和跨格式输出重新生成文档,压缩仅作用于输出副本
|
||||
// 原始模式完整OFD另存保持源字节;选页和跨格式输出重新生成文档,压缩仅作用于输出副本
|
||||
// 输入按需随机读取,输出分块写出;解析对象及后端可能缓存资源,不保证恒定内存
|
||||
// 出错时报告保留已有诊断,调用方应丢弃未完成输出;只有返回nil才表示转换成功
|
||||
// 入参: ctx 取消上下文, source 源数据, size 源字节数, output 输出流, options 转换选项
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
// 来源修改或删除不影响快照,不用于跨文档传输
|
||||
type AnnotationSelection struct {
|
||||
editor *Editor
|
||||
document editorDocumentIdentity
|
||||
data [][]byte
|
||||
resources []string
|
||||
states map[string]map[string]editorCompositeState
|
||||
@@ -38,7 +39,7 @@ func (e *Editor) CaptureAnnotations(page int, ids []string) (*AnnotationSelectio
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selection := &AnnotationSelection{editor: e, states: make(map[string]map[string]editorCompositeState)}
|
||||
selection := &AnnotationSelection{editor: e, document: e.documentIdentity(), states: make(map[string]map[string]editorCompositeState)}
|
||||
seen := make(map[string]bool)
|
||||
for _, id := range ids {
|
||||
if seen[id] {
|
||||
@@ -73,7 +74,7 @@ func (e *Editor) CaptureAnnotations(page int, ids []string) (*AnnotationSelectio
|
||||
// 入参: page 目标页面索引, selection 当前编辑器快照, dx 横向位移,单位为毫米, dy 纵向位移,单位为毫米
|
||||
// 返回: []string 新注解标识, error 错误信息
|
||||
func (e *Editor) PasteAnnotations(page int, selection *AnnotationSelection, dx, dy float64) ([]string, error) {
|
||||
if selection == nil || selection.editor != e || !finite(dx) || !finite(dy) {
|
||||
if selection == nil || selection.editor != e || !e.documentIdentity().matches(selection.document) || !finite(dx) || !finite(dy) {
|
||||
return nil, fmt.Errorf("invalid annotation selection or offset")
|
||||
}
|
||||
var result []string
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
// 父路径的祖先成员增删或排序后需重新捕获,避免使用已失效的序号路径
|
||||
type CompositeSelection struct {
|
||||
editor *Editor
|
||||
document editorDocumentIdentity
|
||||
page string
|
||||
path ObjectPath
|
||||
references int
|
||||
@@ -70,7 +71,7 @@ func (e *Editor) CaptureCompositeObjects(page int, path ObjectPath, indexes []in
|
||||
slices.Sort(indexes)
|
||||
var owner *editorCompositeNode
|
||||
var container *editorXML
|
||||
selection := &CompositeSelection{editor: e, page: e.pages[page].ID, path: ObjectPath{ID: path.ID, Annotation: path.Annotation, Children: slices.Clone(path.Children)}}
|
||||
selection := &CompositeSelection{editor: e, document: e.documentIdentity(), page: e.pages[page].ID, path: ObjectPath{ID: path.ID, Annotation: path.Annotation, Children: slices.Clone(path.Children)}}
|
||||
if path.Annotation == "" {
|
||||
selection.source = e.objectOrigin(path.ID).page
|
||||
} else if e.source != nil {
|
||||
@@ -112,8 +113,8 @@ func (e *Editor) CaptureCompositeObjects(page int, path ObjectPath, indexes []in
|
||||
// 入参: page 页面索引, path 父复合路径, selection 捕获的快照, dx 页面横向位移, dy 页面纵向位移
|
||||
// 返回: []int 新成员序号, error 错误信息
|
||||
func (e *Editor) PasteCompositeObjects(page int, path ObjectPath, selection *CompositeSelection, dx, dy float64) ([]int, error) {
|
||||
if selection == nil || selection.editor != e {
|
||||
return nil, fmt.Errorf("composite selection belongs to another editor")
|
||||
if selection == nil || selection.editor != e || !e.documentIdentity().matches(selection.document) {
|
||||
return nil, fmt.Errorf("composite selection belongs to another document or editor")
|
||||
}
|
||||
if !finite(dx) || !finite(dy) {
|
||||
return nil, fmt.Errorf("paste requires finite offsets")
|
||||
@@ -198,8 +199,8 @@ func (e *Editor) PasteCompositeObjects(page int, path ObjectPath, selection *Com
|
||||
// 入参: selection 独立内部快照, paste 目标范围粘贴操作
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) pasteCompositeSelection(selection *CompositeSelection, paste func([]GraphicObject) error) (err error) {
|
||||
if selection == nil || selection.editor != e {
|
||||
return fmt.Errorf("composite selection belongs to another editor")
|
||||
if selection == nil || selection.editor != e || !e.documentIdentity().matches(selection.document) {
|
||||
return fmt.Errorf("composite selection belongs to another document or editor")
|
||||
}
|
||||
count, maximum := len(e.resources), e.maxID
|
||||
ready := e.source != nil && e.source.idsReady
|
||||
@@ -324,7 +325,7 @@ func (e *Editor) pasteCompositeSelection(selection *CompositeSelection, paste fu
|
||||
object.CompositeGraphicUnit.states = maps.Clone(copy.states)
|
||||
copy.object.CompositeGraphicUnit.states = object.CompositeGraphicUnit.states
|
||||
}
|
||||
object.origin = &editorObjectOrigin{editor: e, page: selection.source, data: copy.data, node: copy.node, object: copy.object}
|
||||
object.origin = &editorObjectOrigin{editor: e, document: e.documentIdentity(), page: selection.source, data: copy.data, node: copy.node, object: copy.object}
|
||||
objects = append(objects, object)
|
||||
}
|
||||
return paste(objects)
|
||||
|
||||
@@ -201,7 +201,7 @@ func (e *Editor) ungroupCompositeNode(renderer *Renderer, node *editorCompositeN
|
||||
return fmt.Errorf("group member cannot be expanded")
|
||||
}
|
||||
}
|
||||
return e.pasteCompositeSelection(&CompositeSelection{editor: e, nodes: members}, func(objects []GraphicObject) error {
|
||||
return e.pasteCompositeSelection(&CompositeSelection{editor: e, document: e.documentIdentity(), nodes: members}, func(objects []GraphicObject) error {
|
||||
ids := make(map[string]string)
|
||||
if copy.ref != nil {
|
||||
for _, object := range objects {
|
||||
@@ -244,7 +244,7 @@ func (e *Editor) ungroupCompositeNode(renderer *Renderer, node *editorCompositeN
|
||||
member.object.state = states[editorObjectID(member.object)]
|
||||
member.object.TextObject.layout = member.object.state.layout
|
||||
member.object.CompositeGraphicUnit.states = states
|
||||
member.object.origin = &editorObjectOrigin{editor: e, data: member.data, node: member.node, object: member.object}
|
||||
member.object.origin = &editorObjectOrigin{editor: e, document: e.documentIdentity(), data: member.data, node: member.node, object: member.object}
|
||||
objects[i] = member.object
|
||||
}
|
||||
return replace(objects)
|
||||
|
||||
@@ -37,7 +37,7 @@ func (e *Editor) copiedObjectStyle(object GraphicObject, layer string) (GraphicO
|
||||
// 返回: *editorObjectOrigin 原文来源
|
||||
func (e *Editor) snapshotOrigin(object GraphicObject) *editorObjectOrigin {
|
||||
if object.origin != nil {
|
||||
if object.origin.editor == e || object.origin.page != nil && e.source != nil && e.source.pages[object.origin.page.ref.ID] == object.origin.page {
|
||||
if object.origin.editor == e && e.documentIdentity().matches(object.origin.document) || object.origin.page != nil && e.source != nil && e.source.pages[object.origin.page.ref.ID] == object.origin.page {
|
||||
return object.origin
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -27,6 +27,15 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// editorDocumentIdentity 标记编辑状态所属的文档,不持有页面或资源数据
|
||||
type editorDocumentIdentity struct {
|
||||
root string
|
||||
id string
|
||||
fresh bool
|
||||
single bool
|
||||
fromNew bool
|
||||
}
|
||||
|
||||
// DocumentCount 获取编辑包内的文档数量,新建编辑器包含一个当前文档
|
||||
// 返回: int 文档数量
|
||||
func (e *Editor) DocumentCount() int {
|
||||
@@ -329,15 +338,31 @@ func (e *Editor) documentRoot() string {
|
||||
// 入参: other 编辑快照
|
||||
// 返回: bool 是否为同一文档
|
||||
func (e *Editor) sameDocument(other *Editor) bool {
|
||||
if e.source == nil || other.source == nil {
|
||||
if e.source == nil && other.source == nil {
|
||||
return e.documentIdentity().matches(other.documentIdentity())
|
||||
}
|
||||
|
||||
// documentIdentity 获取当前文档的入口与原始标识,文档排序不改变身份
|
||||
// 返回: editorDocumentIdentity 文档身份
|
||||
func (e *Editor) documentIdentity() editorDocumentIdentity {
|
||||
if e.source == nil {
|
||||
return editorDocumentIdentity{id: e.Info.DocID, fresh: true}
|
||||
}
|
||||
return editorDocumentIdentity{root: e.documentRoot(), id: e.source.info.DocID, single: e.DocumentCount() == 1, fromNew: e.source.fromNew}
|
||||
}
|
||||
|
||||
// matches 判断两个编辑状态是否属于同一文档,允许新文档首次建立包结构
|
||||
// 入参: other 另一编辑状态的文档身份
|
||||
// 返回: bool 是否为同一文档
|
||||
func (identity editorDocumentIdentity) matches(other editorDocumentIdentity) bool {
|
||||
if identity.fresh || other.fresh {
|
||||
if identity.fresh && other.fresh {
|
||||
return true
|
||||
}
|
||||
fresh, packaged := e, other
|
||||
if fresh.source != nil {
|
||||
fresh, packaged = other, e
|
||||
fresh, packaged := identity, other
|
||||
if !fresh.fresh {
|
||||
fresh, packaged = other, identity
|
||||
}
|
||||
return len(packaged.source.reader.OFD.DocBody) == 1 && (packaged.source.fromNew || fresh.Info.DocID != "" && fresh.Info.DocID == packaged.source.info.DocID)
|
||||
return packaged.single && (packaged.fromNew || fresh.id != "" && fresh.id == packaged.id)
|
||||
}
|
||||
return e.documentRoot() == other.documentRoot() && e.source.info.DocID == other.source.info.DocID
|
||||
return identity.root == other.root && identity.id == other.id
|
||||
}
|
||||
@@ -91,6 +91,11 @@ func (e *Editor) changeFont(ctx context.Context, id string, mode FontMode, file
|
||||
if err != nil || !changed {
|
||||
return err
|
||||
}
|
||||
if mode == FontExternal {
|
||||
if err := pruneExternalFontFiles(ctx, reader, scan.fonts[editorResourceID(id)].location); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return e.commitFontSnapshot(ctx, reader)
|
||||
}
|
||||
|
||||
@@ -133,6 +138,7 @@ func (e *Editor) ProcessFontsWithOptions(ctx context.Context, options FontProces
|
||||
return nil, err
|
||||
}
|
||||
var report []FontChange
|
||||
var externalFiles []string
|
||||
changed := false
|
||||
for document := range reader.DocumentCount() {
|
||||
if err := progress("scan", document, reader.DocumentCount()); err != nil {
|
||||
@@ -172,6 +178,9 @@ func (e *Editor) ProcessFontsWithOptions(ctx context.Context, options FontProces
|
||||
}
|
||||
if item.Changed {
|
||||
changed = true
|
||||
if mode == FontExternal {
|
||||
externalFiles = append(externalFiles, declaration.location)
|
||||
}
|
||||
scan, err = scanEditorFonts(ctx, reader, document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -186,6 +195,9 @@ func (e *Editor) ProcessFontsWithOptions(ctx context.Context, options FontProces
|
||||
if err := progress("commit", 0, 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := pruneExternalFontFiles(ctx, reader, externalFiles...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = e.commitFontSnapshot(ctx, reader)
|
||||
}
|
||||
return report, err
|
||||
@@ -343,11 +355,6 @@ func (e *Editor) changeFontSnapshot(ctx context.Context, scan *editorFontDocumen
|
||||
if err := working.initRoot(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if mode == FontExternal {
|
||||
if err := pruneExternalFontFiles(ctx, &working, declaration.location); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
*scan.reader = working
|
||||
return true, nil
|
||||
}
|
||||
@@ -502,6 +509,9 @@ func (e *Editor) commitFontSnapshot(ctx context.Context, reader *Reader) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
before := e.transactionSnapshot()
|
||||
e.restoreFontSnapshot(*next)
|
||||
if change := e.recordChange(); change != nil {
|
||||
@@ -525,11 +535,22 @@ func (e *Editor) restoreFontSnapshot(state Editor) {
|
||||
*e = state
|
||||
}
|
||||
|
||||
// pruneExternalFontFiles 保留全包仍引用的字体,未确认的扩展引用使清理保持保守
|
||||
// 入参: ctx 取消上下文, reader 包快照, candidate 原字体路径
|
||||
// pruneExternalFontFiles 清理全包不再引用的字体文件,遇到无法识别的扩展时保留资源
|
||||
// 入参: ctx 取消上下文, reader 包快照, candidates 待清理的字体路径
|
||||
// 返回: error 取消或读取错误
|
||||
func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidate string) error {
|
||||
used := make(map[string]bool)
|
||||
func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidates ...string) error {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
removed := make(map[string]bool, len(candidates))
|
||||
for _, candidate := range candidates {
|
||||
if key := strings.ToLower(cleanPackagePath(candidate)); key != "" {
|
||||
removed[key] = true
|
||||
}
|
||||
}
|
||||
if len(removed) == 0 {
|
||||
return nil
|
||||
}
|
||||
for i := range reader.DocumentCount() {
|
||||
files, err := reader.documentFiles(ctx, i)
|
||||
if err != nil {
|
||||
@@ -538,13 +559,13 @@ func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidate strin
|
||||
}
|
||||
return nil
|
||||
}
|
||||
maps.Copy(used, files)
|
||||
for name := range files {
|
||||
delete(removed, name)
|
||||
}
|
||||
removed := make(map[string]bool)
|
||||
key := strings.ToLower(cleanPackagePath(candidate))
|
||||
if used[key] {
|
||||
if len(removed) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for _, name := range reader.fileNamesFold {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
@@ -574,20 +595,21 @@ func pruneExternalFontFiles(ctx context.Context, reader *Reader, candidate strin
|
||||
return true
|
||||
}
|
||||
}
|
||||
if node.matchesOFD("FontFile") && strings.EqualFold(resolveResourcePath(name, base, strings.TrimSpace(editorImportText(data, node))), candidate) {
|
||||
return true
|
||||
if node.matchesOFD("FontFile") {
|
||||
location := resolveResourcePath(name, base, strings.TrimSpace(editorImportText(data, node)))
|
||||
delete(removed, strings.ToLower(cleanPackagePath(location)))
|
||||
}
|
||||
return slices.ContainsFunc(node.children, retains)
|
||||
}
|
||||
if retains(root) {
|
||||
if retains(root) || len(removed) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if !used[key] {
|
||||
removed[key] = true
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
for name := range reader.files {
|
||||
if removed[strings.ToLower(name)] {
|
||||
if removed[strings.ToLower(cleanPackagePath(name))] {
|
||||
delete(reader.files, name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ func (e *Editor) ImportObjects(source *Editor, sourcePage int, ids []string, pag
|
||||
imported[i].TextObject.layout = objects[i].TextObject.layout
|
||||
imported[i].state = objects[i].state
|
||||
imported[i].CompositeGraphicUnit.states = remapCompositeStates(objects[i].CompositeGraphicUnit.states, migration.ids)
|
||||
imported[i].origin = &editorObjectOrigin{editor: editor, data: data, node: root.child("Layer").children[0], object: object}
|
||||
imported[i].origin = &editorObjectOrigin{editor: editor, document: editor.documentIdentity(), data: data, node: root.child("Layer").children[0], object: object}
|
||||
}
|
||||
copied, err = editor.CopyObjects(page, imported, options.DX, options.DY)
|
||||
return err
|
||||
|
||||
@@ -48,6 +48,7 @@ type editorSource struct {
|
||||
// editorObjectOrigin 保留原对象的XML语义,供复制和局部更新复用
|
||||
type editorObjectOrigin struct {
|
||||
editor *Editor
|
||||
document editorDocumentIdentity
|
||||
page *editorSourcePage
|
||||
data []byte
|
||||
node *editorXML
|
||||
@@ -613,6 +614,7 @@ func (e *Editor) setObjectOrigin(id string, origin *editorObjectOrigin) {
|
||||
if origin != nil && origin.page == nil && origin.editor == nil {
|
||||
copy := *origin
|
||||
copy.editor = e
|
||||
copy.document = e.documentIdentity()
|
||||
origin = ©
|
||||
}
|
||||
e.origins[id] = origin
|
||||
|
||||
@@ -20,6 +20,7 @@ import "fmt"
|
||||
// 来源修改、删除或撤销不改变快照,不用于跨文档传输
|
||||
type ObjectSelection struct {
|
||||
editor *Editor
|
||||
document editorDocumentIdentity
|
||||
objects []GraphicObject
|
||||
annotations *AnnotationSelection
|
||||
}
|
||||
@@ -32,7 +33,7 @@ func (e *Editor) CaptureSelection(page int, objects, annotations []string) (*Obj
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
selection := &ObjectSelection{editor: e, objects: items}
|
||||
selection := &ObjectSelection{editor: e, document: e.documentIdentity(), objects: items}
|
||||
if len(annotations) != 0 {
|
||||
selection.annotations, err = e.CaptureAnnotations(page, annotations)
|
||||
if err != nil {
|
||||
@@ -46,7 +47,7 @@ func (e *Editor) CaptureSelection(page int, objects, annotations []string) (*Obj
|
||||
// 入参: page 目标页面索引, selection 当前编辑器快照, dx 横向位移,单位为毫米, dy 纵向位移,单位为毫米
|
||||
// 返回: []string 新正文标识, []string 新注解标识, error 标识、资源或编辑错误
|
||||
func (e *Editor) PasteSelection(page int, selection *ObjectSelection, dx, dy float64) ([]string, []string, error) {
|
||||
if selection == nil || selection.editor != e || !finite(dx) || !finite(dy) {
|
||||
if selection == nil || selection.editor != e || !e.documentIdentity().matches(selection.document) || !finite(dx) || !finite(dy) {
|
||||
return nil, nil, fmt.Errorf("invalid object selection or offset")
|
||||
}
|
||||
var objects, annotations []string
|
||||
@@ -68,8 +69,8 @@ func (e *Editor) PasteSelection(page int, selection *ObjectSelection, dx, dy flo
|
||||
// 入参: page 目标页面索引, path 父复合路径, selection 当前编辑器快照, dx 横向位移,单位为毫米, dy 纵向位移,单位为毫米
|
||||
// 返回: []int 新成员序号, error 选区、资源或编辑错误
|
||||
func (e *Editor) PasteSelectionToComposite(page int, path ObjectPath, selection *ObjectSelection, dx, dy float64) ([]int, error) {
|
||||
if selection == nil || selection.editor != e {
|
||||
return nil, fmt.Errorf("object selection belongs to another editor")
|
||||
if selection == nil || selection.editor != e || !e.documentIdentity().matches(selection.document) {
|
||||
return nil, fmt.Errorf("object selection belongs to another document or editor")
|
||||
}
|
||||
if selection.annotations != nil {
|
||||
return nil, fmt.Errorf("annotations cannot be nested in a page object")
|
||||
|
||||
@@ -207,7 +207,7 @@ func (e *Editor) SetObjectDrawParam(page int, ids []string, id string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updated := &editorObjectOrigin{editor: e, data: data, node: root, object: next}
|
||||
updated := &editorObjectOrigin{editor: e, document: e.documentIdentity(), data: data, node: root, object: next}
|
||||
if origin != nil {
|
||||
updated.page, root.parent = origin.page, origin.node.parent
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ var ErrPDFPassword = pdfgo.ErrPassword
|
||||
// ResolveFile读取PDF引用的外部媒体、附件和页面,默认不访问网络或本地路径
|
||||
// ResolveReference可提供已解密的引用页面,目标阅读器由调用方维护
|
||||
// Halftones提供只读设备命名网屏,所属阅读器由调用方维护,连续色调输出不网屏化
|
||||
// Compression配置转换输出,有损模式按图片实际尺寸降采样,默认及无损保持完整采样
|
||||
// Compression配置转换输出,有损模式按图片实际尺寸降采样,原始及无损模式保持完整采样
|
||||
type PDFImportOptions struct {
|
||||
Password []byte
|
||||
PasswordUTF8 bool
|
||||
|
||||
Reference in new issue
Block a user