mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 16:59:27 +08:00
1 parent
151ddb0a20
commit
d97c6f4e6f
60 files changed
+570
-262
No files matched your search
+80
-18
@@ -57,6 +57,7 @@ type FontInfo = ofdgo.FontInfo
|
||||
type Session struct {
|
||||
Reader *ofdgo.Reader
|
||||
Renderer *ofdgo.Renderer
|
||||
input *ofdgo.Input
|
||||
fontFS *ofdgo.FontFS
|
||||
doc *ofdgo.Document
|
||||
pageCache map[int]*ofdgo.PageContent
|
||||
@@ -424,7 +425,7 @@ type businessEngine struct {
|
||||
copiedObjects *editorClipboard
|
||||
copiedStyle *ofdgo.GraphicObject
|
||||
ctx context.Context
|
||||
resolveSource func(context.Context, any) (io.ReaderAt, int64, error)
|
||||
resolveInput func(context.Context, any) (io.ReaderAt, int64, error)
|
||||
conversionPool *ofdgo.ConversionPool
|
||||
}
|
||||
|
||||
@@ -435,11 +436,22 @@ func Open(data []byte, opts OpenOptions) (*Session, error) {
|
||||
if len(data) == 0 {
|
||||
return nil, fmt.Errorf("empty ofd data")
|
||||
}
|
||||
reader, err := ofdgo.NewReader(bytes.NewReader(data), int64(len(data)), opts.ReaderOptions...)
|
||||
input, err := ofdgo.NewInput(bytes.NewReader(data), int64(len(data)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSession(reader, opts)
|
||||
reader, err := ofdgo.NewReader(input, opts.ReaderOptions...)
|
||||
if err != nil {
|
||||
input.Close()
|
||||
return nil, err
|
||||
}
|
||||
session, err := newSession(reader, opts)
|
||||
if err != nil {
|
||||
input.Close()
|
||||
return nil, err
|
||||
}
|
||||
session.input = input
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// Close 关闭文档会话
|
||||
@@ -449,11 +461,16 @@ func (s *Session) Close() error {
|
||||
return nil
|
||||
}
|
||||
reader := s.Reader
|
||||
input := s.input
|
||||
*s = Session{}
|
||||
var err error
|
||||
if reader != nil {
|
||||
return reader.Close()
|
||||
err = reader.Close()
|
||||
}
|
||||
return nil
|
||||
if input != nil {
|
||||
err = errors.Join(err, input.Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Document 创建指定文档的会话,保留编辑内容和字体配置,不复用页面缓存
|
||||
@@ -1318,8 +1335,13 @@ func (e *businessEngine) detectDocumentFormat(args []platformValue) (any, error)
|
||||
if len(args) != 3 {
|
||||
return nil, fmt.Errorf("missing detection arguments")
|
||||
}
|
||||
source := &browserReaderAt{read: args[0], size: int64(args[1].Float()), checkpoint: args[2]}
|
||||
return ofdgo.DetectFormat(source, source.size)
|
||||
reader := &browserReaderAt{read: args[0], size: int64(args[1].Float()), checkpoint: args[2]}
|
||||
input, err := ofdgo.NewInput(reader, reader.size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer input.Close()
|
||||
return ofdgo.DetectFormat(input)
|
||||
}
|
||||
|
||||
// convertPDFDocument 通过库转换PDF,不替换当前文档会话
|
||||
@@ -1329,10 +1351,11 @@ func (e *businessEngine) convertPDFDocument(args []platformValue) (any, error) {
|
||||
if len(args) != 6 {
|
||||
return nil, fmt.Errorf("missing PDF data")
|
||||
}
|
||||
source, size, err := e.sourceFromValue(args[0])
|
||||
input, err := e.inputFromValue(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer input.Close()
|
||||
fonts, err := e.fontsFromJS(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1353,7 +1376,7 @@ func (e *businessEngine) convertPDFDocument(args []platformValue) (any, error) {
|
||||
}))
|
||||
}
|
||||
options.RendererOptions = []ofdgo.RendererOption{ofdgo.WithFontFS(fontSources...)}
|
||||
editor, report, err := ofdgo.ImportPDF(e.operationContext(), source, size, options)
|
||||
editor, report, err := ofdgo.ImportPDF(e.operationContext(), input, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1454,8 +1477,13 @@ func (e *businessEngine) convertFile(args []platformValue) (any, error) {
|
||||
} else if err := awaitExport(args[5], "open", "document."+format.Extension); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
source := &browserReaderAt{read: args[0], checkpoint: args[6], size: int64(args[1].Float())}
|
||||
report, err := ofdgo.Convert(e.newOutputContext(args[6]), source, source.size, writer, options)
|
||||
reader := &browserReaderAt{read: args[0], checkpoint: args[6], size: int64(args[1].Float())}
|
||||
input, err := ofdgo.NewInput(reader, reader.size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer input.Close()
|
||||
report, err := ofdgo.Convert(e.newOutputContext(args[6]), input, writer, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1483,16 +1511,29 @@ func (e *businessEngine) packFiles(args []platformValue) (any, error) {
|
||||
writer := bufio.NewWriterSize(exportWriter{write: args[2]}, 1<<20)
|
||||
files := args[0]
|
||||
entries := make([]ofdgo.ArchiveFile, files.Length())
|
||||
inputs := make([]*browserReaderAt, files.Length())
|
||||
defer func() {
|
||||
for _, entry := range entries {
|
||||
if entry.Input != nil {
|
||||
entry.Input.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
for i := 0; i < files.Length(); i++ {
|
||||
file := files.Index(i)
|
||||
name := file.Get("name").String()
|
||||
read := args[1].Invoke(i)
|
||||
source := &browserReaderAt{read: read, checkpoint: args[4], size: int64(file.Get("size").Float())}
|
||||
entries[i] = ofdgo.ArchiveFile{Name: name, Source: source, Size: source.size}
|
||||
reader := &browserReaderAt{read: read, checkpoint: args[4], size: int64(file.Get("size").Float())}
|
||||
input, err := ofdgo.NewInput(reader, reader.size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputs[i] = reader
|
||||
entries[i] = ofdgo.ArchiveFile{Name: name, Input: input}
|
||||
}
|
||||
options := ofdgo.ArchiveOptions{OnProgress: func(completed, total int) error {
|
||||
if completed > 0 {
|
||||
entries[completed-1].Source.(*browserReaderAt).data = nil
|
||||
inputs[completed-1].data = nil
|
||||
}
|
||||
return awaitExport(args[3], completed, total)
|
||||
}}
|
||||
@@ -1517,10 +1558,16 @@ func (e *businessEngine) openDocument(args []platformValue) (any, error) {
|
||||
if len(args) < 3 {
|
||||
return nil, fmt.Errorf("missing open arguments")
|
||||
}
|
||||
source, size, err := e.sourceFromValue(args[0])
|
||||
input, err := e.inputFromValue(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keep := false
|
||||
defer func() {
|
||||
if !keep {
|
||||
input.Close()
|
||||
}
|
||||
}()
|
||||
fonts, err := e.fontsFromJS(args[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1535,7 +1582,7 @@ func (e *businessEngine) openDocument(args []platformValue) (any, error) {
|
||||
defer clear(credentials.Password)
|
||||
options.ReaderOptions = append(options.ReaderOptions, ofdgo.WithCredentials(credentials))
|
||||
}
|
||||
reader, err := ofdgo.NewReader(source, size, options.ReaderOptions...)
|
||||
reader, err := ofdgo.NewReader(input, options.ReaderOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1552,6 +1599,8 @@ func (e *businessEngine) openDocument(args []platformValue) (any, error) {
|
||||
e.currentSession = nil
|
||||
}
|
||||
e.currentSession = session
|
||||
session.input = input
|
||||
keep = true
|
||||
return e.currentSession.Summary(), nil
|
||||
}
|
||||
|
||||
@@ -5456,10 +5505,16 @@ func (e *businessEngine) loadImport(args []platformValue) (any, error) {
|
||||
if args[0].IsNull() {
|
||||
return nil, nil
|
||||
}
|
||||
source, size, err := e.sourceFromValue(args[0])
|
||||
input, err := e.inputFromValue(args[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keep := false
|
||||
defer func() {
|
||||
if !keep {
|
||||
input.Close()
|
||||
}
|
||||
}()
|
||||
var options []ofdgo.ReaderOption
|
||||
if len(args) > 1 && !args[1].IsNull() && !args[1].IsUndefined() {
|
||||
credentials, err := e.credentialsFromJS(args[1])
|
||||
@@ -5469,10 +5524,11 @@ func (e *businessEngine) loadImport(args []platformValue) (any, error) {
|
||||
defer clear(credentials.Password)
|
||||
options = append(options, ofdgo.WithCredentials(credentials))
|
||||
}
|
||||
reader, err := ofdgo.NewReader(source, size, options...)
|
||||
reader, err := ofdgo.NewReader(input, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader.Closer = input
|
||||
doc, err := reader.Doc()
|
||||
if err != nil {
|
||||
reader.Close()
|
||||
@@ -5483,6 +5539,7 @@ func (e *businessEngine) loadImport(args []platformValue) (any, error) {
|
||||
return nil, fmt.Errorf("document has no pages")
|
||||
}
|
||||
e.pendingImport = reader
|
||||
keep = true
|
||||
entries := make([]DocumentEntry, reader.DocumentCount())
|
||||
signed := doc.Signatures != ""
|
||||
for index, body := range reader.OFD.DocBody {
|
||||
@@ -6123,6 +6180,7 @@ func (e *businessEngine) selectDocumentVersion(args []platformValue) (any, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session.input, e.currentSession.input = e.currentSession.input, nil
|
||||
_ = e.currentSession.Close()
|
||||
e.currentSession = session
|
||||
e.clearImport()
|
||||
@@ -6280,6 +6338,7 @@ func (e *businessEngine) selectDocument(args []platformValue) (any, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session.input, e.currentSession.input = e.currentSession.input, nil
|
||||
_ = e.currentSession.Close()
|
||||
e.currentSession, e.currentEditor = session, session.editor
|
||||
if e.pendingImport != nil {
|
||||
@@ -6380,6 +6439,9 @@ func (e *businessEngine) previewEditor(editor *ofdgo.Editor, annotations bool) (
|
||||
session.Renderer.SetFontFS(session.fontFS)
|
||||
editor.SetFontFS(session.fontFS)
|
||||
}
|
||||
if editor == e.currentEditor {
|
||||
session.input, e.currentSession.input = e.currentSession.input, nil
|
||||
}
|
||||
_ = e.currentSession.Close()
|
||||
}
|
||||
editor.SetRenderBackends(session.Renderer.Backends())
|
||||
|
||||
@@ -89,7 +89,7 @@ type NativeEngine struct {
|
||||
// nativeReader 在会话当前操作上下文中分块读取,不捕获已结束请求的上下文
|
||||
type nativeReader struct {
|
||||
engine *NativeEngine
|
||||
source io.ReaderAt
|
||||
input io.ReaderAt
|
||||
}
|
||||
|
||||
// nativeCall 保存单次worker适配状态
|
||||
@@ -139,7 +139,7 @@ func NewNativeEngine(resolve NativeResolver) *NativeEngine { return &NativeEngin
|
||||
func (e *NativeEngine) Call(ctx context.Context, name string, args []any, emit func(any)) (result map[string]any) {
|
||||
e.once.Do(func() {
|
||||
e.gate = make(chan struct{}, 1)
|
||||
e.resolveSource = e.source
|
||||
e.resolveInput = e.input
|
||||
e.conversionPool = e.ConversionPool
|
||||
})
|
||||
if ctx == nil {
|
||||
@@ -236,7 +236,7 @@ func (e *NativeEngine) Call(ctx context.Context, name string, args []any, emit f
|
||||
|
||||
// Close 释放会话,不关闭宿主持有的ReaderAt资源
|
||||
func (e *NativeEngine) Close() error {
|
||||
e.once.Do(func() { e.gate = make(chan struct{}, 1); e.resolveSource = e.source })
|
||||
e.once.Do(func() { e.gate = make(chan struct{}, 1); e.resolveInput = e.input })
|
||||
e.gate <- struct{}{}
|
||||
defer func() { <-e.gate }()
|
||||
e.closed = true
|
||||
@@ -253,8 +253,8 @@ func (e *NativeEngine) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// source 读取授权源并以宿主报告的大小为准
|
||||
func (e *NativeEngine) source(ctx context.Context, value any) (io.ReaderAt, int64, error) {
|
||||
// input 读取授权源并以宿主报告的大小为准
|
||||
func (e *NativeEngine) input(ctx context.Context, value any) (io.ReaderAt, int64, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -262,7 +262,7 @@ func (e *NativeEngine) source(ctx context.Context, value any) (io.ReaderAt, int6
|
||||
value = p.value
|
||||
}
|
||||
if data, ok := value.([]byte); ok {
|
||||
return nativeReader{engine: e, source: bytes.NewReader(data)}, int64(len(data)), nil
|
||||
return nativeReader{engine: e, input: bytes.NewReader(data)}, int64(len(data)), nil
|
||||
}
|
||||
descriptor, ok := value.(map[string]any)
|
||||
if !ok {
|
||||
@@ -272,14 +272,14 @@ func (e *NativeEngine) source(ctx context.Context, value any) (io.ReaderAt, int6
|
||||
if !ok || token == "" || e.Resolve == nil {
|
||||
return nil, 0, fmt.Errorf("unauthorized native file")
|
||||
}
|
||||
source, size, err := e.Resolve(token)
|
||||
input, size, err := e.Resolve(token)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if source == nil || size < 0 {
|
||||
return nil, 0, fmt.Errorf("invalid native file source")
|
||||
if input == nil || size < 0 {
|
||||
return nil, 0, fmt.Errorf("invalid native file input")
|
||||
}
|
||||
return nativeReader{engine: e, source: source}, size, nil
|
||||
return nativeReader{engine: e, input: input}, size, nil
|
||||
}
|
||||
|
||||
// ReadAt 分块传递取消检查,宿主ReadAt自身阻塞时由宿主控制超时
|
||||
@@ -290,7 +290,7 @@ func (r nativeReader) ReadAt(data []byte, offset int64) (int, error) {
|
||||
return total, err
|
||||
}
|
||||
n := min(len(data), 256<<10)
|
||||
got, err := r.source.ReadAt(data[:n], offset)
|
||||
got, err := r.input.ReadAt(data[:n], offset)
|
||||
total += got
|
||||
if err != nil {
|
||||
return total, err
|
||||
@@ -307,7 +307,7 @@ func (r nativeReader) ReadAt(data []byte, offset int64) (int, error) {
|
||||
// 入参: ctx 调用上下文, value 资源参数
|
||||
// 返回: []byte 资源数据, error 读取错误
|
||||
func (e *NativeEngine) readBytes(ctx context.Context, value any) ([]byte, error) {
|
||||
source, size, err := e.source(ctx, value)
|
||||
input, size, err := e.input(ctx, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -320,7 +320,7 @@ func (e *NativeEngine) readBytes(ctx context.Context, value any) ([]byte, error)
|
||||
return nil, err
|
||||
}
|
||||
n := int(min(int64(256<<10), size-offset))
|
||||
got, err := source.ReadAt(output[int(offset):int(offset)+n], offset)
|
||||
got, err := input.ReadAt(output[int(offset):int(offset)+n], offset)
|
||||
if got != n {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -540,7 +540,7 @@ func (c *nativeCall) progress(kind, stage string, phased bool) nativeCallback {
|
||||
|
||||
// readCallback 创建有界随机读取回调
|
||||
func (c *nativeCall) readCallback(value any) (nativeCallback, int64, error) {
|
||||
source, size, err := c.engine.source(c.ctx, value)
|
||||
input, size, err := c.engine.input(c.ctx, value)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
@@ -553,7 +553,7 @@ func (c *nativeCall) readCallback(value any) (nativeCallback, int64, error) {
|
||||
return nil, fmt.Errorf("invalid native read range")
|
||||
}
|
||||
data := make([]byte, n)
|
||||
got, err := source.ReadAt(data, offset)
|
||||
got, err := input.ReadAt(data, offset)
|
||||
if got != n {
|
||||
return nil, io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -876,9 +876,13 @@ func nativeOutputBase(value platformValue) string {
|
||||
return base
|
||||
}
|
||||
|
||||
// sourceFromValue 读取宿主授权的随机读取源
|
||||
func (e *businessEngine) sourceFromValue(value platformValue) (io.ReaderAt, int64, error) {
|
||||
return e.resolveSource(e.operationContext(), value.value)
|
||||
// inputFromValue 读取宿主授权的随机读取源
|
||||
func (e *businessEngine) inputFromValue(value platformValue) (*ofdgo.Input, error) {
|
||||
reader, size, err := e.resolveInput(e.operationContext(), value.value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ofdgo.NewInput(reader, size)
|
||||
}
|
||||
|
||||
// platformValueOf 包装参数
|
||||
@@ -1024,11 +1028,12 @@ func (e *businessEngine) bytesFromJS(v platformValue) ([]byte, error) {
|
||||
if data, ok := v.value.([]byte); ok {
|
||||
return append([]byte(nil), data...), nil
|
||||
}
|
||||
source, size, err := e.sourceFromValue(v)
|
||||
input, err := e.inputFromValue(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return io.ReadAll(io.NewSectionReader(source, 0, size))
|
||||
defer input.Close()
|
||||
return io.ReadAll(io.NewSectionReader(input, 0, input.Size()))
|
||||
}
|
||||
|
||||
// successResult 保留二进制返回结构
|
||||
|
||||
@@ -21,8 +21,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"syscall/js"
|
||||
|
||||
"github.com/xiaoqidun/ofdgo"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -220,8 +221,11 @@ func platformParseJSON(data []byte) map[string]any {
|
||||
return result
|
||||
}
|
||||
|
||||
// sourceFromValue 读取浏览器二进制源
|
||||
func (e *businessEngine) sourceFromValue(value platformValue) (io.ReaderAt, int64, error) {
|
||||
// inputFromValue 读取浏览器二进制源
|
||||
func (e *businessEngine) inputFromValue(value platformValue) (*ofdgo.Input, error) {
|
||||
data, err := e.bytesFromJS(value)
|
||||
return bytes.NewReader(data), int64(len(data)), err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ofdgo.NewInput(bytes.NewReader(data), int64(len(data)))
|
||||
}
|
||||
Reference in new issue
Block a user