// Copyright 2025-2026 肖其顿 (XIAO QI DUN) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ofdgo import ( "context" ) // pdfObjectChunkLimit 限制导入暂存片段的容量,不限制页面对象数量 const pdfObjectChunkLimit = 256 // pdfObjectBuffer 分块收集导入对象,提交前只整理一次连续数据 type pdfObjectBuffer struct { parts [][]GraphicObject size int } // objectValidation 在单次导入中共享不可变资源的校验结果,隔离局部编辑器 // 返回: *editorValidation 当前编辑器的构建校验会话 func (p *pdfImporter) objectValidation() *editorValidation { if p.validation == nil || p.validation.Editor != p.editor { p.validation = &editorValidation{Editor: p.editor} } return p.validation } // appendObject 追加本次导入对象,按需建立独立暂存缓冲 // 入参: object 独立导入对象 func (p *pdfImporter) appendObject(object GraphicObject) { if p.objects == nil { p.objects = &pdfObjectBuffer{} } p.objects.append(object) } // append 追加对象,小批量连续存储,超过阈值后不复制前序片段 // 入参: object 独立对象 func (b *pdfObjectBuffer) append(object GraphicObject) { index := len(b.parts) - 1 if index == 0 && len(b.parts[0]) == cap(b.parts[0]) && cap(b.parts[0]) < pdfObjectChunkLimit { part := b.parts[0] next := make([]GraphicObject, len(part), min(pdfObjectChunkLimit, max(1, cap(part)*2))) copy(next, part) b.parts[0] = next } if index < 0 || len(b.parts[index]) == cap(b.parts[index]) { capacity := pdfObjectChunkLimit if index < 0 { capacity = 1 } b.parts = append(b.parts, make([]GraphicObject, 0, capacity)) index++ } b.parts[index] = append(b.parts[index], object) b.size++ } // len 获取暂存对象数量,空缓冲返回零 // 返回: int 对象数量 func (b *pdfObjectBuffer) len() int { if b == nil { return 0 } return b.size } // truncate 回滚到已有对象位置,清理丢弃对象及片段引用 // 入参: size 保留对象数量 func (b *pdfObjectBuffer) truncate(size int) { if size < 0 || size > b.len() { panic("invalid PDF object buffer size") } if size == b.len() { return } remaining := size for index, part := range b.parts { if remaining <= len(part) { clear(part[remaining:]) b.parts[index] = part[:remaining] clear(b.parts[index+1:]) b.parts = b.parts[:index+1] b.size = size return } remaining -= len(part) } } // data 按绘制顺序整理独立对象数组,取消时保留原片段供重试 // 入参: ctx 取消上下文 // 返回: []GraphicObject 暂存数据, error 取消错误 func (b *pdfObjectBuffer) data(ctx context.Context) ([]GraphicObject, error) { if err := ctx.Err(); err != nil { return nil, err } if b.len() == 0 { return nil, nil } if len(b.parts) == 1 { return b.parts[0][:b.size:b.size], nil } objects := make([]GraphicObject, b.size) offset := 0 for _, part := range b.parts { if err := ctx.Err(); err != nil { return nil, err } offset += copy(objects[offset:], part) } if err := ctx.Err(); err != nil { return nil, err } b.parts[0] = objects clear(b.parts[1:]) b.parts = b.parts[:1] return objects, nil } // commitObjects 将当前页面待转换对象写入编辑文档 // 返回: error 对象写入错误 func (p *pdfImporter) commitObjects() error { if p.objects.len() == 0 { return nil } objects, err := p.objects.data(p.ctx) if err != nil { return err } objects, err = p.sharedClipObjects(objects) if err != nil { return err } if err := p.objectValidation().appendOwnedObjects(p.ctx, p.page, objects); err != nil { return err } p.objects = nil return nil }