mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 08:46:55 +08:00
1 parent
151ddb0a20
commit
d97c6f4e6f
60 files changed
+570
-262
No files matched your search
@@ -121,6 +121,7 @@ func main() {
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@@ -129,7 +130,12 @@ import (
|
||||
|
||||
func main() {
|
||||
// 1. 打开文档
|
||||
reader, err := ofdgo.Open("test.ofd")
|
||||
input, err := ofdgo.OpenFile(context.Background(), "test.ofd", ofdgo.InputOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input.Close()
|
||||
reader, err := ofdgo.NewReader(input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -166,15 +172,11 @@ import (
|
||||
|
||||
func main() {
|
||||
// 1. 打开文档
|
||||
pdfFile, err := os.Open("test.pdf")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer pdfFile.Close()
|
||||
info, err := pdfFile.Stat()
|
||||
input, err := ofdgo.OpenFile(context.Background(), "test.pdf", ofdgo.InputOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input.Close()
|
||||
// 2. 创建文件
|
||||
ofdFile, err := os.Create("test.ofd")
|
||||
if err != nil {
|
||||
@@ -182,7 +184,7 @@ func main() {
|
||||
}
|
||||
defer ofdFile.Close()
|
||||
// 3. 转换文档
|
||||
report, err := ofdgo.ConvertPDF(context.Background(), pdfFile, info.Size(), ofdFile, ofdgo.PDFImportOptions{})
|
||||
report, err := ofdgo.ConvertPDF(context.Background(), input, ofdFile, ofdgo.PDFImportOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -198,6 +200,7 @@ func main() {
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@@ -206,7 +209,12 @@ import (
|
||||
|
||||
func main() {
|
||||
// 1. 打开OFD文件
|
||||
reader, err := ofdgo.Open("test.ofd")
|
||||
input, err := ofdgo.OpenFile(context.Background(), "test.ofd", ofdgo.InputOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input.Close()
|
||||
reader, err := ofdgo.NewReader(input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -230,6 +238,7 @@ func main() {
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
@@ -238,12 +247,13 @@ import (
|
||||
|
||||
func main() {
|
||||
// 1. 打开OFD文件
|
||||
data, err := os.ReadFile("test.ofd")
|
||||
input, err := ofdgo.OpenFile(context.Background(), "test.ofd", ofdgo.InputOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input.Close()
|
||||
// 2. 验证OFD签名
|
||||
reports, err := ofdgo.VerifySignaturesBytes(data)
|
||||
reports, err := ofdgo.VerifySignatures(input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -276,6 +286,7 @@ func main() {
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
@@ -285,7 +296,12 @@ import (
|
||||
|
||||
func main() {
|
||||
// 1. 打开OFD文件
|
||||
reader, err := ofdgo.Open("invoice.ofd")
|
||||
input, err := ofdgo.OpenFile(context.Background(), "invoice.ofd", ofdgo.InputOptions{})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer input.Close()
|
||||
reader, err := ofdgo.NewReader(input)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -52,6 +52,28 @@ type Writer struct {
|
||||
closed bool
|
||||
}
|
||||
|
||||
// NewTemp 创建私有输入缓存文件,调用方负责关闭和删除
|
||||
// 入参: directory 缓存目录,空值使用系统临时目录
|
||||
// 返回: *os.File 缓存文件, error 创建错误
|
||||
func NewTemp(directory string) (*os.File, error) {
|
||||
if directory == "" {
|
||||
directory = os.TempDir()
|
||||
}
|
||||
for range 100 {
|
||||
n, err := rand.Int(rand.Reader, big.NewInt(10000000))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := filepath.Join(directory, fmt.Sprintf("ofdgo-%07d.tmp", n.Int64()))
|
||||
file, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
continue
|
||||
}
|
||||
return file, err
|
||||
}
|
||||
return nil, os.ErrExist
|
||||
}
|
||||
|
||||
// NewTarget 记录目标当前状态,允许目标尚不存在
|
||||
// 入参: name 目标路径
|
||||
// 返回: *Target 保存目标, error 路径或文件错误
|
||||
|
||||
@@ -96,7 +96,7 @@ func pngColorProfile(ctx context.Context, data []byte) ([]byte, *pngColor, error
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
profile, err := io.ReadAll(io.LimitReader(contextInput{ReadCloser: reader, context: ctx}, profileLimit+1))
|
||||
profile, err := io.ReadAll(io.LimitReader(contextInput{ReadCloser: reader, ctx: ctx}, profileLimit+1))
|
||||
closeErr := reader.Close()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -139,7 +139,7 @@ func (img *EncodedImage) decodeContext(ctx context.Context, pending chan struct{
|
||||
close(pending)
|
||||
img.mu.Unlock()
|
||||
}()
|
||||
pixels, _, err := image.Decode(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(img.data)), context: ctx})
|
||||
pixels, _, err := image.Decode(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(img.data)), ctx: ctx})
|
||||
if err == nil && (img.format == "jpeg" || img.profile != nil) {
|
||||
var profile *pdfgo.ColorSpace
|
||||
if img.profile != nil {
|
||||
@@ -184,7 +184,7 @@ func newEncodedImage(data []byte) (*EncodedImage, error) {
|
||||
// 入参: ctx 取消上下文, data 只读图片编码
|
||||
// 返回: *EncodedImage 惰性图片, error 头信息、格式或取消错误
|
||||
func newEncodedImageContext(ctx context.Context, data []byte) (*EncodedImage, error) {
|
||||
config, format, err := image.DecodeConfig(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
config, format, err := image.DecodeConfig(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if cancelErr := ctx.Err(); cancelErr != nil {
|
||||
return nil, cancelErr
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ func DecodeContext(ctx context.Context, data []byte) (img image.Image, format st
|
||||
img, err := DecodeTIFF(ctx, data, layout)
|
||||
return img, "tiff", err
|
||||
}
|
||||
img, format, err = image.Decode(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
img, format, err = image.Decode(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err == nil {
|
||||
return img, NormalizeFormat(format), nil
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func DecodeConfigDataContext(ctx context.Context, data []byte) (config image.Con
|
||||
cfg.Width, cfg.Height = size.X, size.Y
|
||||
return cfg, "tiff", nil
|
||||
}
|
||||
cfg, format, err := image.DecodeConfig(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
cfg, format, err := image.DecodeConfig(contextInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err == nil {
|
||||
return cfg, NormalizeFormat(format), nil
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@ import (
|
||||
// contextInput 在读取图像编码时检查调用方取消状态
|
||||
type contextInput struct {
|
||||
io.ReadCloser
|
||||
context context.Context
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Read 检查取消状态后读取原始数据
|
||||
// 入参: data 数据缓冲区
|
||||
// 返回: int 读取字节数, error 读取或取消错误
|
||||
func (r contextInput) Read(data []byte) (int, error) {
|
||||
if err := r.context.Err(); err != nil {
|
||||
if err := r.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.ReadCloser.Read(data)
|
||||
|
||||
@@ -165,7 +165,7 @@ func readTIFFDirectory(ctx context.Context, input io.Reader, order binary.ByteOr
|
||||
if offset < position {
|
||||
return result, position, ErrTIFFRandomAccess
|
||||
}
|
||||
reader := contextInput{ReadCloser: io.NopCloser(input), context: ctx}
|
||||
reader := contextInput{ReadCloser: io.NopCloser(input), ctx: ctx}
|
||||
if _, err := io.CopyN(io.Discard, reader, offset-position); err != nil {
|
||||
return result, position, err
|
||||
}
|
||||
|
||||
@@ -211,11 +211,11 @@ func (s *u3dLineState) accumulateCorners(mesh *U3DMesh, shadingIndex uint32, cor
|
||||
}
|
||||
|
||||
// value 按指定上下文和位宽读取点集或线集字段
|
||||
// 入参: context 压缩上下文, small 是否为8位字段
|
||||
// 入参: ctx 压缩上下文, small 是否为8位字段
|
||||
// 返回: uint32 字段值
|
||||
func (r *u3dLineReader) value(context int, small bool) uint32 {
|
||||
func (r *u3dLineReader) value(ctx int, small bool) uint32 {
|
||||
if r.bits != nil {
|
||||
return r.bits.dynamicValue(&r.hist[context], small)
|
||||
return r.bits.dynamicValue(&r.hist[ctx], small)
|
||||
}
|
||||
if small {
|
||||
return uint32(r.values.u8())
|
||||
|
||||
@@ -167,15 +167,15 @@ func (r *u3dResourceReader) motionFrame(frame *U3DKeyFrame, previous U3DKeyFrame
|
||||
}
|
||||
|
||||
// motionDifference 用共享幅值上下文恢复一至三个差分分量
|
||||
// 入参: previous 前一帧分量, scale 逆量化系数, context 符号上下文,下一项为幅值上下文
|
||||
// 入参: previous 前一帧分量, scale 逆量化系数, ctx 符号上下文,下一项为幅值上下文
|
||||
// 返回: [3]float32 重建分量, error 符号、数值或读取错误
|
||||
func (r *u3dResourceReader) motionDifference(previous []float32, scale float32, context int) (values [3]float32, err error) {
|
||||
signs := r.value(context, true)
|
||||
func (r *u3dResourceReader) motionDifference(previous []float32, scale float32, ctx int) (values [3]float32, err error) {
|
||||
signs := r.value(ctx, true)
|
||||
if signs >= 1<<len(previous) {
|
||||
return values, fmt.Errorf("invalid U3D motion difference signs")
|
||||
}
|
||||
for index, prior := range previous {
|
||||
difference := float64(r.value(context+1, false)) * float64(scale)
|
||||
difference := float64(r.value(ctx+1, false)) * float64(scale)
|
||||
if signs&(1<<index) != 0 {
|
||||
difference = -difference
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ type u3dResourceReader struct {
|
||||
}
|
||||
|
||||
// value 读取指定上下文的压缩字段,首次使用时初始化算术区间
|
||||
// 入参: context 压缩上下文, small 是否为8位字段
|
||||
// 入参: ctx 压缩上下文, small 是否为8位字段
|
||||
// 返回: uint32 字段值
|
||||
func (r *u3dResourceReader) value(context int, small bool) uint32 {
|
||||
func (r *u3dResourceReader) value(ctx int, small bool) uint32 {
|
||||
if r.err() != nil {
|
||||
return 0
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func (r *u3dResourceReader) value(context int, small bool) uint32 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return r.bits.dynamicValue(&r.hist[context], small)
|
||||
return r.bits.dynamicValue(&r.hist[ctx], small)
|
||||
}
|
||||
|
||||
// u32 读取普通32位字段,压缩开始后沿用当前算术区间
|
||||
|
||||
+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)))
|
||||
}
|
||||
@@ -475,11 +475,11 @@ func (h *Host) call(ctx context.Context, name string, value any) (any, error) {
|
||||
}
|
||||
descriptor, _ := args["data"].(map[string]any)
|
||||
key, _ := descriptor["nativeFile"].(string)
|
||||
source, size, err := h.files.resolve(key)
|
||||
input, size, err := h.files.resolve(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.Copy(writer, io.NewSectionReader(source, 0, size))
|
||||
_, err = io.Copy(writer, io.NewSectionReader(input, 0, size))
|
||||
return nil, err
|
||||
case "fonts":
|
||||
h.fontOnce.Do(h.readFonts)
|
||||
|
||||
@@ -17,18 +17,50 @@ package ofdgo
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
basefile "github.com/xiaoqidun/ofdgo/internal/base/file"
|
||||
)
|
||||
|
||||
// Open 打开OFD文件
|
||||
// 返回的阅读器持有文件,使用完毕后需调用Close
|
||||
// 入参: path 文件路径, options 阅读选项
|
||||
// 返回: *Reader 阅读器实例, error 错误信息
|
||||
func Open(path string, options ...ReaderOption) (*Reader, error) {
|
||||
// Input 统一文件、内存及顺序流的随机读取,可并发读取和关闭,不可复制
|
||||
// 调用方应在阅读、编辑及转换结束后调用Close,借入的数据需在此期间保持不变
|
||||
// 借入的随机读取器应支持并发ReadAt,Close不会关闭调用方传入的读取器
|
||||
type Input struct {
|
||||
mu sync.RWMutex
|
||||
reader io.ReaderAt
|
||||
size int64
|
||||
path string
|
||||
file *os.File
|
||||
temp bool
|
||||
}
|
||||
|
||||
// InputOptions 设置顺序输入的缓存及大小限制,单位为字节
|
||||
// MemoryLimit为0时使用8MiB,浏览器WASM仅用内存;负值表示仅用内存
|
||||
// TempDir为空时使用系统临时目录,不自动创建调用方指定的目录
|
||||
// MaxSize为0时不限制输入大小,OpenFile和ReadInput均检查此限制
|
||||
// 无临时文件系统的平台可显式使用仅内存模式,并通过MaxSize限制输入
|
||||
type InputOptions struct {
|
||||
MemoryLimit int64
|
||||
MaxSize int64
|
||||
TempDir string
|
||||
}
|
||||
|
||||
// OpenFile 打开文件输入,不复制文件内容,Close释放文件句柄
|
||||
// 入参: ctx 取消上下文, path 文件路径, options 输入选项
|
||||
// 返回: *Input 输入源, error 打开或大小错误
|
||||
func OpenFile(ctx context.Context, path string, options InputOptions) (*Input, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := basefile.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -38,34 +70,215 @@ func Open(path string, options ...ReaderOption) (*Reader, error) {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
r, err := zip.NewReader(f, info.Size())
|
||||
if !info.Mode().IsRegular() {
|
||||
f.Close()
|
||||
return nil, fmt.Errorf("input is not a regular file")
|
||||
}
|
||||
input, err := NewInput(f, info.Size())
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
reader := &Reader{
|
||||
Path: path,
|
||||
Zip: r,
|
||||
Closer: f,
|
||||
}
|
||||
if err := reader.open(options); err != nil {
|
||||
reader.Close()
|
||||
input.file, input.path = f, path
|
||||
if err := input.checkOptions(ctx, options); err != nil {
|
||||
input.Close()
|
||||
return nil, err
|
||||
}
|
||||
return reader, nil
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// NewReader 从IO读取器创建OFD阅读器
|
||||
// 读取器的生命周期由调用方管理,Reader.Close不会关闭它
|
||||
// 入参: r IO读取器, size 数据大小, options 阅读选项
|
||||
// NewInput 借用指定长度的随机读取源,不复制数据或接管其生命周期
|
||||
// 入参: reader 随机读取器, size 数据大小
|
||||
// 返回: *Input 输入源, error 参数错误
|
||||
func NewInput(reader io.ReaderAt, size int64) (*Input, error) {
|
||||
if reader == nil || size < 0 {
|
||||
return nil, fmt.Errorf("invalid input")
|
||||
}
|
||||
return &Input{reader: reader, size: size}, nil
|
||||
}
|
||||
|
||||
// ReadInput 从当前位置接收顺序输入,超过内存阈值后转入临时文件
|
||||
// 已知大小且支持随机访问的可定位读取器直接复用,不移动其当前位置
|
||||
// 不关闭传入流;取消无法中断阻塞的Read,调用方应自行设置超时或关闭流
|
||||
// 入参: ctx 取消上下文, reader 输入流, options 输入选项
|
||||
// 返回: *Input 输入源, error 读取、缓存或取消错误
|
||||
func ReadInput(ctx context.Context, reader io.Reader, options InputOptions) (*Input, error) {
|
||||
if reader == nil || options.MaxSize < 0 {
|
||||
return nil, fmt.Errorf("invalid input options")
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if random, ok := reader.(io.ReaderAt); ok {
|
||||
if seeker, ok := reader.(io.Seeker); ok {
|
||||
var size int64 = -1
|
||||
if sized, ok := reader.(interface{ Size() int64 }); ok {
|
||||
size = sized.Size()
|
||||
} else if stated, ok := reader.(interface{ Stat() (os.FileInfo, error) }); ok {
|
||||
info, err := stated.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Mode().IsRegular() {
|
||||
size = info.Size()
|
||||
}
|
||||
}
|
||||
if size >= 0 {
|
||||
offset, err := seeker.Seek(0, io.SeekCurrent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if offset < 0 || offset > size {
|
||||
return nil, fmt.Errorf("invalid input offset")
|
||||
}
|
||||
input, err := NewInput(io.NewSectionReader(random, offset, size-offset), size-offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := input.checkOptions(ctx, options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
limit := options.MemoryLimit
|
||||
if limit == 0 {
|
||||
limit = 8 << 20
|
||||
if runtime.GOOS == "js" {
|
||||
limit = -1
|
||||
}
|
||||
}
|
||||
input := &Input{}
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
input.Close()
|
||||
}
|
||||
}()
|
||||
var memory bytes.Buffer
|
||||
buffer := make([]byte, 64<<10)
|
||||
empty := 0
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n, err := reader.Read(buffer)
|
||||
if n < 0 || n > len(buffer) {
|
||||
return nil, fmt.Errorf("invalid input read count")
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
if failure := ctx.Err(); failure != nil {
|
||||
return nil, failure
|
||||
}
|
||||
if n > 0 {
|
||||
empty = 0
|
||||
if input.size > int64(^uint64(0)>>1)-int64(n) || options.MaxSize > 0 && int64(n) > options.MaxSize-input.size {
|
||||
return nil, fmt.Errorf("input exceeds size limit")
|
||||
}
|
||||
if input.file == nil && limit >= 0 && input.size+int64(n) > limit {
|
||||
file, failure := basefile.NewTemp(options.TempDir)
|
||||
if failure != nil {
|
||||
return nil, failure
|
||||
}
|
||||
input.file, input.temp = file, true
|
||||
if _, failure = file.Write(memory.Bytes()); failure != nil {
|
||||
return nil, failure
|
||||
}
|
||||
memory = bytes.Buffer{}
|
||||
}
|
||||
var output io.Writer = &memory
|
||||
if input.file != nil {
|
||||
output = input.file
|
||||
}
|
||||
if _, failure := output.Write(buffer[:n]); failure != nil {
|
||||
return nil, failure
|
||||
}
|
||||
input.size += int64(n)
|
||||
} else if err == nil {
|
||||
empty++
|
||||
if empty >= 100 {
|
||||
return nil, io.ErrNoProgress
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
input.reader = bytes.NewReader(memory.Bytes())
|
||||
if input.file != nil {
|
||||
input.reader = input.file
|
||||
}
|
||||
success = true
|
||||
return input, nil
|
||||
}
|
||||
|
||||
// checkOptions 检查取消状态及输入大小
|
||||
// 入参: ctx 取消上下文, options 输入选项
|
||||
// 返回: error 取消或大小错误
|
||||
func (s *Input) checkOptions(ctx context.Context, options InputOptions) error {
|
||||
if options.MaxSize < 0 || options.MaxSize > 0 && s.size > options.MaxSize {
|
||||
return fmt.Errorf("input exceeds size limit")
|
||||
}
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
// Size 返回输入字节数,关闭后仍可查询
|
||||
// 返回: int64 数据大小
|
||||
func (s *Input) Size() int64 {
|
||||
return s.size
|
||||
}
|
||||
|
||||
// ReadAt 读取指定位置的数据,关闭后返回os.ErrClosed
|
||||
// 入参: data 目标缓冲区, offset 字节偏移
|
||||
// 返回: int 读取字节数, error 读取错误
|
||||
func (s *Input) ReadAt(data []byte, offset int64) (int, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
if s.reader == nil {
|
||||
return 0, os.ErrClosed
|
||||
}
|
||||
return io.NewSectionReader(s.reader, 0, s.size).ReadAt(data, offset)
|
||||
}
|
||||
|
||||
// Close 释放自有文件和缓存,等待正在进行的读取结束,可重复调用
|
||||
// 返回: error 文件关闭或临时文件删除错误
|
||||
func (s *Input) Close() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.reader = nil
|
||||
if s.file == nil {
|
||||
return nil
|
||||
}
|
||||
file := s.file
|
||||
s.file = nil
|
||||
err := file.Close()
|
||||
if s.temp {
|
||||
err = errors.Join(err, os.Remove(file.Name()))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NewReader 从统一输入源创建OFD阅读器,不接管输入源生命周期
|
||||
// 阅读器及其派生编辑器使用结束后,调用方再关闭输入源
|
||||
// 入参: input 输入源, options 阅读选项
|
||||
// 返回: *Reader 阅读器实例, error 错误信息
|
||||
func NewReader(r io.ReaderAt, size int64, options ...ReaderOption) (*Reader, error) {
|
||||
zr, err := zip.NewReader(r, size)
|
||||
func NewReader(input *Input, options ...ReaderOption) (*Reader, error) {
|
||||
if input == nil {
|
||||
return nil, fmt.Errorf("missing input")
|
||||
}
|
||||
zr, err := zip.NewReader(input, input.Size())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := &Reader{
|
||||
Zip: zr,
|
||||
Path: input.path,
|
||||
Zip: zr,
|
||||
}
|
||||
if err := reader.open(options); err != nil {
|
||||
return nil, err
|
||||
|
||||
+2
-2
@@ -276,7 +276,7 @@ func (r *Reader) parseAnnotations(ctx context.Context, doc *Document) error {
|
||||
continue
|
||||
}
|
||||
var pageAnnot PageAnnot
|
||||
err = xml.NewDecoder(imageInput{ReadCloser: af, context: ctx}).Decode(&pageAnnot)
|
||||
err = xml.NewDecoder(imageInput{ReadCloser: af, ctx: ctx}).Decode(&pageAnnot)
|
||||
_ = af.Close()
|
||||
if err != nil {
|
||||
continue
|
||||
@@ -310,7 +310,7 @@ func (r *Reader) annotationIndexContext(ctx context.Context, doc *Document) (Ann
|
||||
}
|
||||
defer f.Close()
|
||||
var annotations Annotations
|
||||
if err := xml.NewDecoder(imageInput{ReadCloser: f, context: ctx}).Decode(&annotations); err != nil {
|
||||
if err := xml.NewDecoder(imageInput{ReadCloser: f, ctx: ctx}).Decode(&annotations); err != nil {
|
||||
return Annotations{}, err
|
||||
}
|
||||
pageIDs := make(map[string]string, len(doc.Pages.Page))
|
||||
|
||||
+7
-8
@@ -27,9 +27,8 @@ import (
|
||||
|
||||
// ArchiveFile 描述已生成的归档文件,不关闭调用方提供的源读取器
|
||||
type ArchiveFile struct {
|
||||
Name string
|
||||
Source io.ReaderAt
|
||||
Size int64
|
||||
Name string
|
||||
Input *Input
|
||||
}
|
||||
|
||||
// ArchiveOptions 设置ZIP封装,压缩不重新编码归档内已生成的文档
|
||||
@@ -47,13 +46,13 @@ func WriteArchive(ctx context.Context, writer io.Writer, files []ArchiveFile, op
|
||||
}
|
||||
names := map[string]bool{}
|
||||
for _, file := range files {
|
||||
if !fs.ValidPath(file.Name) || file.Name == "." || strings.Contains(file.Name, "\\") || names[file.Name] || file.Source == nil || file.Size < 0 {
|
||||
if !fs.ValidPath(file.Name) || file.Name == "." || strings.Contains(file.Name, "\\") || names[file.Name] || file.Input == nil {
|
||||
return 0, fmt.Errorf("invalid archive file %q", file.Name)
|
||||
}
|
||||
names[file.Name] = true
|
||||
}
|
||||
var count int64
|
||||
archive := zip.NewWriter(convertWriter{context: ctx, writer: writer, count: &count})
|
||||
archive := zip.NewWriter(convertWriter{ctx: ctx, writer: writer, count: &count})
|
||||
configureArchive(archive, options.Compression)
|
||||
var buffer []byte
|
||||
for i, file := range files {
|
||||
@@ -77,15 +76,15 @@ func WriteArchive(ctx context.Context, writer io.Writer, files []ArchiveFile, op
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
input := convertReader{context: ctx, source: file.Source}
|
||||
input := convertReader{ctx: ctx, input: file.Input}
|
||||
if buffer == nil {
|
||||
buffer = make([]byte, 32<<10)
|
||||
}
|
||||
written, err := io.CopyBuffer(entry, io.NewSectionReader(input, 0, file.Size), buffer)
|
||||
written, err := io.CopyBuffer(entry, io.NewSectionReader(input, 0, file.Input.Size()), buffer)
|
||||
if err != nil {
|
||||
return count, err
|
||||
}
|
||||
if written != file.Size {
|
||||
if written != file.Input.Size() {
|
||||
return count, io.ErrUnexpectedEOF
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func (e *Editor) WriteToWithOptions(ctx context.Context, writer io.Writer, optio
|
||||
return 0, err
|
||||
}
|
||||
var count int64
|
||||
return snapshot.WriteTo(convertWriter{context: ctx, writer: writer, count: &count})
|
||||
return snapshot.WriteTo(convertWriter{ctx: ctx, writer: writer, count: &count})
|
||||
}
|
||||
|
||||
// WritePagesToWithOptions 按指定顺序另存页面,仅为所选页面准备资源和压缩计划
|
||||
@@ -116,7 +116,7 @@ func (e *Editor) WritePagesToWithOptions(ctx context.Context, writer io.Writer,
|
||||
snapshot.OnWriteProgress = editorProgress(e.OnWriteProgress).withContext(ctx)
|
||||
snapshot.output = &outputOptimization{ctx: ctx, options: options.Compression}
|
||||
var count int64
|
||||
return snapshot.WritePagesTo(convertWriter{context: ctx, writer: writer, count: &count}, indexes)
|
||||
return snapshot.WritePagesTo(convertWriter{ctx: ctx, writer: writer, count: &count}, indexes)
|
||||
}
|
||||
|
||||
// outputSnapshot 创建只读输出配置,原签名存在时仅调整ZIP封装
|
||||
@@ -211,7 +211,7 @@ func (r *Reader) compressionImageSafety(ctx context.Context, images []ImageInfo)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
buffer := bufio.NewReader(imageInput{ReadCloser: input, context: ctx})
|
||||
buffer := bufio.NewReader(imageInput{ReadCloser: input, ctx: ctx})
|
||||
found, err := editorXMLPrefix(buffer)
|
||||
if err != nil || !found && (structured || strings.EqualFold(path.Ext(name), ".xml")) {
|
||||
safe = false
|
||||
@@ -432,7 +432,7 @@ func (e *Editor) compressOutputFile(file *zip.File, buffer []byte, limit int) ([
|
||||
deflater.Reset(output)
|
||||
}
|
||||
defer deflater.Close()
|
||||
source := imageInput{ReadCloser: input, context: e.output.ctx}
|
||||
source := imageInput{ReadCloser: input, ctx: e.output.ctx}
|
||||
_, err = io.CopyBuffer(deflater, source, buffer)
|
||||
if output.exceeded {
|
||||
_, err = io.CopyBuffer(io.Discard, source, buffer)
|
||||
@@ -542,7 +542,7 @@ func (e *Editor) writeOutputFile(archive *zip.Writer, file *zip.File, buffer []b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.CopyBuffer(entry, imageInput{ReadCloser: io.NopCloser(payload), context: e.output.ctx}, buffer); err != nil {
|
||||
if _, err := io.CopyBuffer(entry, imageInput{ReadCloser: io.NopCloser(payload), ctx: e.output.ctx}, buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
e.output.completed++
|
||||
|
||||
@@ -208,7 +208,7 @@ func imageWorkingSet(ctx context.Context, data []byte, limit int) int {
|
||||
}
|
||||
return len(data) + tiffImageWorkingSet(layout, available)
|
||||
}
|
||||
config, _, err := baseimage.DecodeConfig(imageInput{ReadCloser: io.NopCloser(input), context: ctx})
|
||||
config, _, err := baseimage.DecodeConfig(imageInput{ReadCloser: io.NopCloser(input), ctx: ctx})
|
||||
if err != nil || config.Width <= 0 || config.Height <= 0 {
|
||||
return limit
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func (e *Editor) compressPatternPaints() error {
|
||||
}
|
||||
hash := sha256.New()
|
||||
var size int64
|
||||
x := newOFDXML(convertWriter{writer: hash, count: &size, context: e.output.ctx})
|
||||
x := newOFDXML(convertWriter{writer: hash, count: &size, ctx: e.output.ctx})
|
||||
x.ctx = e.output.ctx
|
||||
x.color("FillColor", fill)
|
||||
x.color("StrokeColor", (*FillColor)(stroke))
|
||||
|
||||
@@ -108,7 +108,7 @@ func optimizeBMPResource(ctx context.Context, data []byte, options CompressionOp
|
||||
if depth == 16 || compression == 3 {
|
||||
pixels, err = baseimage.DecodeBMPContext(ctx, data)
|
||||
} else {
|
||||
pixels, _, err = image.Decode(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
pixels, _, err = image.Decode(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -460,7 +460,7 @@ func outputResourceDigest(ctx context.Context, reader *Reader, parts map[string]
|
||||
}
|
||||
}
|
||||
hash := sha256.New()
|
||||
_, err := io.CopyBuffer(hash, imageInput{ReadCloser: input, context: ctx}, buffer)
|
||||
_, err := io.CopyBuffer(hash, imageInput{ReadCloser: input, ctx: ctx}, buffer)
|
||||
closeErr := input.Close()
|
||||
if err != nil {
|
||||
return [32]byte{}, err
|
||||
|
||||
+26
-25
@@ -84,15 +84,15 @@ type ConvertReport struct {
|
||||
|
||||
// convertReader 将取消上下文传递到随机读取
|
||||
type convertReader struct {
|
||||
context context.Context
|
||||
source io.ReaderAt
|
||||
ctx context.Context
|
||||
input io.ReaderAt
|
||||
}
|
||||
|
||||
// convertWriter 将取消上下文传递到写入并统计实际字节数
|
||||
type convertWriter struct {
|
||||
context context.Context
|
||||
writer io.Writer
|
||||
count *int64
|
||||
ctx context.Context
|
||||
writer io.Writer
|
||||
count *int64
|
||||
}
|
||||
|
||||
// OutputFormats 返回独立的输出格式列表,不依赖WebUI或具体渲染后端
|
||||
@@ -113,9 +113,9 @@ func OutputFormats() []OutputFormat {
|
||||
// 原始模式完整OFD另存保持源字节;选页和跨格式输出重新生成文档,压缩仅作用于输出副本
|
||||
// 输入按需随机读取,输出分块写出;解析对象及后端可能缓存资源,不保证恒定内存
|
||||
// 出错时报告保留已有诊断,调用方应丢弃未完成输出;只有返回nil才表示转换成功
|
||||
// 入参: ctx 取消上下文, source 源数据, size 源字节数, output 输出流, options 转换选项
|
||||
// 入参: ctx 取消上下文, input 输入源, output 输出流, options 转换选项
|
||||
// 返回: ConvertReport 转换报告, error 错误信息
|
||||
func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writer, options ConvertOptions) (report ConvertReport, err error) {
|
||||
func Convert(ctx context.Context, input *Input, output io.Writer, options ConvertOptions) (report ConvertReport, err error) {
|
||||
if options.Pool != nil {
|
||||
if err = options.Pool.acquire(ctx, false, 0); err != nil {
|
||||
return report, err
|
||||
@@ -174,13 +174,14 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
if err = progress("open", 0, 0); err != nil {
|
||||
return report, err
|
||||
}
|
||||
if source == nil || size <= 0 {
|
||||
return report, fmt.Errorf("empty document source")
|
||||
if input == nil || input.Size() <= 0 {
|
||||
return report, fmt.Errorf("empty document input")
|
||||
}
|
||||
source = convertReader{context: ctx, source: source}
|
||||
size := input.Size()
|
||||
operationInput := &Input{reader: convertReader{ctx: ctx, input: input}, size: size}
|
||||
report.Input = strings.ToLower(strings.TrimSpace(options.Input))
|
||||
if report.Input == "" {
|
||||
report.Input, err = DetectFormat(source, size)
|
||||
report.Input, err = DetectFormat(operationInput)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
@@ -199,7 +200,7 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
return report, fmt.Errorf("missing output writer")
|
||||
}
|
||||
if report.Input == "pdf" && format == "pdf" && options.Compression.Mode != CompressionUnchanged && options.Pages == nil && strings.TrimSpace(options.PageRange) == "" {
|
||||
pdfReader, openErr := pdfgo.NewReaderWithOptions(source, size, pdfgo.ReaderOptions{Password: options.PDF.Password, PasswordUTF8: options.PDF.PasswordUTF8})
|
||||
pdfReader, openErr := pdfgo.NewReaderWithOptions(operationInput, size, pdfgo.ReaderOptions{Password: options.PDF.Password, PasswordUTF8: options.PDF.PasswordUTF8})
|
||||
if openErr != nil {
|
||||
return report, openErr
|
||||
}
|
||||
@@ -233,13 +234,13 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
}
|
||||
return nil
|
||||
}
|
||||
editor, report.PDF, err = ImportPDF(ctx, source, size, pdfOptions)
|
||||
editor, report.PDF, err = ImportPDF(ctx, operationInput, pdfOptions)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
count = editor.PageCount()
|
||||
case "ofd":
|
||||
reader, err = NewReader(source, size, options.ReaderOptions...)
|
||||
reader, err = NewReader(operationInput, options.ReaderOptions...)
|
||||
if err != nil {
|
||||
return report, err
|
||||
}
|
||||
@@ -260,13 +261,13 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
return report, err
|
||||
}
|
||||
report.Pages = len(indices)
|
||||
writer := convertWriter{context: ctx, writer: output, count: &report.Bytes}
|
||||
writer := convertWriter{ctx: ctx, writer: output, count: &report.Bytes}
|
||||
if format == "ofd" {
|
||||
if options.Pages == nil && editor == nil && options.Fonts == FontKeep && options.Compression.Mode == CompressionUnchanged {
|
||||
if err = progress("write", 0, 0); err != nil {
|
||||
return report, err
|
||||
}
|
||||
_, err = io.Copy(writer, io.NewSectionReader(source, 0, size))
|
||||
_, err = io.Copy(writer, io.NewSectionReader(operationInput, 0, size))
|
||||
report.Unchanged = err == nil
|
||||
return report, err
|
||||
}
|
||||
@@ -301,7 +302,7 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
if err = progress("write", 0, 0); err != nil {
|
||||
return report, err
|
||||
}
|
||||
_, err = io.Copy(writer, io.NewSectionReader(source, 0, size))
|
||||
_, err = io.Copy(writer, io.NewSectionReader(operationInput, 0, size))
|
||||
report.Unchanged = err == nil
|
||||
return report, err
|
||||
}
|
||||
@@ -352,7 +353,7 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
return report, readErr
|
||||
}
|
||||
if err = options.PageOutput(index, func(output io.Writer) error {
|
||||
return renderer.RenderTo(page, convertWriter{context: ctx, writer: output, count: &report.Bytes}, format)
|
||||
return renderer.RenderTo(page, convertWriter{ctx: ctx, writer: output, count: &report.Bytes}, format)
|
||||
}); err != nil {
|
||||
return report, fmt.Errorf("export page %d: %w", index+1, err)
|
||||
}
|
||||
@@ -366,12 +367,12 @@ func Convert(ctx context.Context, source io.ReaderAt, size int64, output io.Writ
|
||||
// 入参: data 目标缓冲, offset 源偏移
|
||||
// 返回: int 读取字节数, error 读取错误
|
||||
func (r convertReader) ReadAt(data []byte, offset int64) (int, error) {
|
||||
if err := r.context.Err(); err != nil {
|
||||
if err := r.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := r.source.ReadAt(data, offset)
|
||||
n, err := r.input.ReadAt(data, offset)
|
||||
if err == nil {
|
||||
err = r.context.Err()
|
||||
err = r.ctx.Err()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -380,7 +381,7 @@ func (r convertReader) ReadAt(data []byte, offset int64) (int, error) {
|
||||
// 入参: data 待写数据
|
||||
// 返回: int 写入字节数, error 写入错误
|
||||
func (w convertWriter) Write(data []byte) (int, error) {
|
||||
if err := w.context.Err(); err != nil {
|
||||
if err := w.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := w.writer.Write(data)
|
||||
@@ -389,7 +390,7 @@ func (w convertWriter) Write(data []byte) (int, error) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err == nil {
|
||||
err = w.context.Err()
|
||||
err = w.ctx.Err()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -398,7 +399,7 @@ func (w convertWriter) Write(data []byte) (int, error) {
|
||||
// 入参: data 待写文本
|
||||
// 返回: int 写入字节数, error 写入、短写或取消错误
|
||||
func (w convertWriter) WriteString(data string) (int, error) {
|
||||
if err := w.context.Err(); err != nil {
|
||||
if err := w.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := io.WriteString(w.writer, data)
|
||||
@@ -407,7 +408,7 @@ func (w convertWriter) WriteString(data string) (int, error) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err == nil {
|
||||
err = w.context.Err()
|
||||
err = w.ctx.Err()
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -64,7 +64,7 @@ func (e *Editor) writeEncrypted(writer io.Writer) (int64, error) {
|
||||
return 0, err
|
||||
}
|
||||
defer clear(plaintext.Bytes())
|
||||
r, err := NewReader(bytes.NewReader(plaintext.Bytes()), int64(plaintext.Len()))
|
||||
r, err := NewReader(&Input{reader: bytes.NewReader(plaintext.Bytes()), size: int64(plaintext.Len())})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ type encryptionEntry struct {
|
||||
// 入参: data 明文OFD包, options 加密策略
|
||||
// 返回: []byte 加密OFD包, error 错误信息
|
||||
func EncryptPackage(data []byte, options EncryptionOptions) ([]byte, error) {
|
||||
r, err := NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
r, err := NewReader(&Input{reader: bytes.NewReader(data), size: int64(len(data))})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -66,10 +66,10 @@ func (e *Editor) SetDocumentActions(actions []Action) error {
|
||||
return err
|
||||
}
|
||||
patch := editorXMLPatch{root.close, root.close, encoded}
|
||||
context := root
|
||||
ctx := root
|
||||
if node := root.child("Actions"); node != nil {
|
||||
patch.start, patch.end = node.start, node.end
|
||||
context = node
|
||||
ctx = node
|
||||
} else {
|
||||
for _, child := range root.children {
|
||||
if child.matchesOFD(child.name.Local) && slices.Contains([]string{"VPreferences", "Bookmarks", "Annotations", "CustomTags", "Attachments", "Extensions"}, child.name.Local) {
|
||||
@@ -78,7 +78,7 @@ func (e *Editor) SetDocumentActions(actions []Action) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
patch.data, err = editorXMLGenerated(encoded, context.name.Space)
|
||||
patch.data, err = editorXMLGenerated(encoded, ctx.name.Space)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -396,19 +396,19 @@ func editorBookmarkDefinition(data []byte, name string, next *Bookmark, add bool
|
||||
}
|
||||
container := root.child("Bookmarks")
|
||||
if add {
|
||||
context := container
|
||||
if context == nil {
|
||||
context = root
|
||||
ctx := container
|
||||
if ctx == nil {
|
||||
ctx = root
|
||||
}
|
||||
attrs, err := next.Dest.attributes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dest, err := context.containerXML("Dest", attrs, nil)
|
||||
dest, err := ctx.containerXML("Dest", attrs, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item, err := context.containerXML("Bookmark", ofdAttrs{{Name: xml.Name{Local: "Name"}, Value: next.Name}}, dest)
|
||||
item, err := ctx.containerXML("Bookmark", ofdAttrs{{Name: xml.Name{Local: "Name"}, Value: next.Name}}, dest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ func (e *Editor) prepareFontBinding(ctx context.Context, scan *editorFontDocumen
|
||||
}
|
||||
file, node := text.file, text.node
|
||||
var object TextObject
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(file.data[node.start:node.end])), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(file.data[node.start:node.end])), ctx: ctx})
|
||||
if err := decoder.Decode(&object); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (r *Reader) fontCleanupCandidate(ctx context.Context, name string) (bool, e
|
||||
} else if file, ok := r.packageFile(name); ok {
|
||||
size = file.UncompressedSize64
|
||||
}
|
||||
candidate, err := fontCleanupDirectory(imageInput{ReadCloser: input, context: ctx}, size)
|
||||
candidate, err := fontCleanupDirectory(imageInput{ReadCloser: input, ctx: ctx}, size)
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
err = nil
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func editorBinaryImage(ctx context.Context, data []byte, img image.Image) ([]byt
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
var err error
|
||||
img, _, err = image.Decode(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
img, _, err = image.Decode(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
|
||||
@@ -185,9 +185,9 @@ func editorDocInfoXML(data []byte, docID string, index int) ([]byte, *editorXML,
|
||||
// 返回: []byte 修改后的XML, error 编码错误
|
||||
func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byte, error) {
|
||||
container := info.child("CustomDatas")
|
||||
context := info
|
||||
ctx := info
|
||||
if container != nil {
|
||||
context = container
|
||||
ctx = container
|
||||
}
|
||||
var items []CustomData
|
||||
if values != nil {
|
||||
@@ -226,7 +226,7 @@ func editorCustomDatas(data []byte, info *editorXML, values *CustomDatas) ([]byt
|
||||
if node == nil {
|
||||
var value bytes.Buffer
|
||||
_ = xml.EscapeText(&value, []byte(item.Value))
|
||||
entry, err := context.containerXML("CustomData", ofdAttrs{{Name: xml.Name{Local: "Name"}, Value: item.Name}}, value.Bytes())
|
||||
entry, err := ctx.containerXML("CustomData", ofdAttrs{{Name: xml.Name{Local: "Name"}, Value: item.Name}}, value.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -205,17 +205,17 @@ func (e *Editor) SetOutlineActions(path []int, actions []Action) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
context := node
|
||||
ctx := node
|
||||
if existing := node.child("Actions"); existing != nil {
|
||||
context = existing
|
||||
ctx = existing
|
||||
}
|
||||
encoded, err = editorXMLGenerated(encoded, context.name.Space)
|
||||
encoded, err = editorXMLGenerated(encoded, ctx.name.Space)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var patch editorXMLPatch
|
||||
if context != node {
|
||||
patch = editorXMLPatch{context.start, context.end, encoded}
|
||||
if ctx != node {
|
||||
patch = editorXMLPatch{ctx.start, ctx.end, encoded}
|
||||
} else {
|
||||
if node.open != node.end {
|
||||
encoded = append(encoded, data[node.open:node.close]...)
|
||||
|
||||
@@ -101,7 +101,7 @@ func (e *Editor) sourcePartsPrepared(ctx context.Context, progress editorProgres
|
||||
}
|
||||
generated[name] = known
|
||||
if (reader.DocumentCount() > 1 || reader.OFD.DocBody[reader.documentIndex].versioned) && !known.stagedLeaf(nil) || bound && known.objects == nil {
|
||||
input := imageInput{ReadCloser: data.open(), context: ctx}
|
||||
input := imageInput{ReadCloser: data.open(), ctx: ctx}
|
||||
parts[name], err = io.ReadAll(input)
|
||||
input.Close()
|
||||
if err != nil {
|
||||
@@ -454,15 +454,15 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
|
||||
}
|
||||
}
|
||||
var added []byte
|
||||
context := source.root.child("Content")
|
||||
if context == nil {
|
||||
context = source.root
|
||||
ctx := source.root.child("Content")
|
||||
if ctx == nil {
|
||||
ctx = source.root
|
||||
}
|
||||
for _, layer := range page.Content.Layer[len(source.original.Content.Layer):] {
|
||||
if len(layer.Objects) == 0 {
|
||||
continue
|
||||
}
|
||||
encoded, err := e.sourceLayerXML(layer, context.name.Space)
|
||||
encoded, err := e.sourceLayerXML(layer, ctx.name.Space)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -471,7 +471,7 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
|
||||
if len(added) != 0 {
|
||||
content := source.root.child("Content")
|
||||
if content == nil {
|
||||
value, err := context.containerXML("Content", nil, added)
|
||||
value, err := ctx.containerXML("Content", nil, added)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -492,12 +492,12 @@ func (e *Editor) sourcePageXML(index int, source *editorSourcePage) ([]byte, err
|
||||
return nil, err
|
||||
}
|
||||
patch := editorXMLPatch{source.root.close, source.root.close, encoded}
|
||||
context := source.root
|
||||
ctx := source.root
|
||||
if node := source.root.child("Actions"); node != nil {
|
||||
patch.start, patch.end = node.start, node.end
|
||||
context = node
|
||||
ctx = node
|
||||
}
|
||||
patch.data, err = editorXMLGenerated(encoded, context.name.Space)
|
||||
patch.data, err = editorXMLGenerated(encoded, ctx.name.Space)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func (e *Editor) pruneDocumentReferences(ctx context.Context, parts map[string][
|
||||
return err
|
||||
}
|
||||
} else if known := generated[name]; data == nil && known.staged != nil {
|
||||
input := imageInput{ReadCloser: known.staged.open(), context: ctx}
|
||||
input := imageInput{ReadCloser: known.staged.open(), ctx: ctx}
|
||||
var err error
|
||||
data, err = io.ReadAll(input)
|
||||
input.Close()
|
||||
@@ -196,7 +196,7 @@ func (e *Editor) pruneDocumentReferences(ctx context.Context, parts map[string][
|
||||
// 入参: ctx 取消上下文, data 创作XML, removed 已删除页面标识
|
||||
// 返回: []byte 更新后的XML, error 解析或取消错误
|
||||
func pruneCreatedPageReferences(ctx context.Context, data []byte, removed map[string]bool) ([]byte, error) {
|
||||
d := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
d := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
var stack []pageReferenceNode
|
||||
var patches []editorXMLPatch
|
||||
root := false
|
||||
|
||||
@@ -184,7 +184,7 @@ func editorResourceDefinitions(ctx context.Context, data []byte, name string, so
|
||||
result.definitions = make(map[string]*editorResourceRefs)
|
||||
outer := &editorResourceRefs{ids: make(map[string]bool), files: make(map[string]bool), fonts: make(map[string]*editorFontUsage)}
|
||||
scan := editorReferenceScan{ctx: ctx, refs: outer, name: name, safe: true}
|
||||
input := bufio.NewReader(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
input := bufio.NewReader(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if found, err := editorXMLPrefix(input); err != nil || !found {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func (c *resourceCatalog) styleDefinition(ctx context.Context, item ResourceInfo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(target); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -488,7 +488,7 @@ func validateResourceColors(ctx context.Context, catalog *resourceCatalog, space
|
||||
}
|
||||
if resourceColorNode(node) && sameResourceID(id, space.ID) {
|
||||
var paint FillColor
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(file.data[node.start:node.end])), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(file.data[node.start:node.end])), ctx: ctx})
|
||||
if err := decoder.Decode(&paint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ func (r *editorResourceRefs) scanContext(ctx context.Context, input io.Reader, n
|
||||
if ctx.Err() != nil {
|
||||
return false, false
|
||||
}
|
||||
input = imageInput{ReadCloser: io.NopCloser(input), context: ctx}
|
||||
input = imageInput{ReadCloser: io.NopCloser(input), ctx: ctx}
|
||||
}
|
||||
buffer := bufio.NewReader(input)
|
||||
found, err := editorXMLPrefix(buffer)
|
||||
|
||||
@@ -37,14 +37,14 @@ type ImageInfo struct {
|
||||
|
||||
// imageOutput 在分块写出图片时检查调用方取消状态
|
||||
type imageOutput struct {
|
||||
context context.Context
|
||||
writer io.Writer
|
||||
ctx context.Context
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// imageInput 在读取资源数据时检查调用方取消状态
|
||||
type imageInput struct {
|
||||
io.ReadCloser
|
||||
context context.Context
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// imageResourceKey 合并有效十进制标识,非标准标识保留原值,不相互混淆
|
||||
@@ -61,7 +61,7 @@ func imageResourceKey(id string) string {
|
||||
// 入参: data 数据缓冲区
|
||||
// 返回: int 读取字节数, error 读取或取消错误
|
||||
func (r imageInput) Read(data []byte) (int, error) {
|
||||
if err := r.context.Err(); err != nil {
|
||||
if err := r.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.ReadCloser.Read(data)
|
||||
@@ -71,7 +71,7 @@ func (r imageInput) Read(data []byte) (int, error) {
|
||||
// 入参: data 数据块
|
||||
// 返回: int 写入字节数, error 写入或取消错误
|
||||
func (w imageOutput) Write(data []byte) (int, error) {
|
||||
if err := w.context.Err(); err != nil {
|
||||
if err := w.ctx.Err(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return w.writer.Write(data)
|
||||
@@ -128,7 +128,7 @@ func (r *Reader) Images(ctx context.Context) ([]ImageInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
var res Res
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(&res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -185,7 +185,7 @@ func (r *Reader) Image(ctx context.Context, id string) (ImageInfo, error) {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
var res Res
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(&res); err != nil {
|
||||
return ImageInfo{}, err
|
||||
}
|
||||
@@ -233,7 +233,7 @@ func (r *Reader) OpenImage(ctx context.Context, id string) (io.ReadCloser, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return imageInput{ReadCloser: input, context: ctx}, nil
|
||||
return imageInput{ReadCloser: input, ctx: ctx}, nil
|
||||
}
|
||||
|
||||
// WriteImages 将原始图片逐个写入ZIP,不改变图片编码,文件序号避免同名覆盖
|
||||
|
||||
+7
-7
@@ -24,16 +24,16 @@ import (
|
||||
|
||||
// DetectFormat 按文件内容识别PDF或OFD,不读取压缩内容或关闭输入
|
||||
// 识别结果不代表文档完整有效,仍需通过对应阅读器解析
|
||||
// 入参: source 随机读取源, size 文件字节数
|
||||
// 入参: input 输入源
|
||||
// 返回: string 格式标识pdf或ofd, error 读取错误或不支持的格式
|
||||
func DetectFormat(source io.ReaderAt, size int64) (string, error) {
|
||||
if source == nil || size <= 0 {
|
||||
return "", fmt.Errorf("empty document source")
|
||||
func DetectFormat(input *Input) (string, error) {
|
||||
if input == nil || input.Size() <= 0 {
|
||||
return "", fmt.Errorf("empty document input")
|
||||
}
|
||||
source = io.NewSectionReader(source, 0, size)
|
||||
size := input.Size()
|
||||
var header [1024]byte
|
||||
data := header[:min(size, int64(len(header)))]
|
||||
n, err := source.ReadAt(data, 0)
|
||||
n, err := input.ReadAt(data, 0)
|
||||
if err != nil && err != io.EOF {
|
||||
return "", err
|
||||
}
|
||||
@@ -43,7 +43,7 @@ func DetectFormat(source io.ReaderAt, size int64) (string, error) {
|
||||
if bytes.HasPrefix(data, []byte("%PDF-")) {
|
||||
return "pdf", nil
|
||||
}
|
||||
archive, err := zip.NewReader(source, size)
|
||||
archive, err := zip.NewReader(input, size)
|
||||
if err == nil {
|
||||
reader := &Reader{Zip: archive}
|
||||
if err := reader.indexPackage(); err != nil {
|
||||
|
||||
+1
-1
@@ -344,7 +344,7 @@ func (r *Reader) invoiceData(ctx context.Context, location string) ([]byte, erro
|
||||
return nil, err
|
||||
}
|
||||
defer stream.Close()
|
||||
data, err := io.ReadAll(io.LimitReader(imageInput{ReadCloser: stream, context: ctx}, invoiceXMLLimit+1))
|
||||
data, err := io.ReadAll(io.LimitReader(imageInput{ReadCloser: stream, ctx: ctx}, invoiceXMLLimit+1))
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ func packageDataEqual(ctx context.Context, reader *Reader, name string, data []b
|
||||
return false, err
|
||||
}
|
||||
defer stream.Close()
|
||||
input := imageInput{ReadCloser: stream, context: ctx}
|
||||
input := imageInput{ReadCloser: stream, ctx: ctx}
|
||||
var buffer [32 << 10]byte
|
||||
for len(data) != 0 {
|
||||
block := buffer[:min(len(buffer), len(data))]
|
||||
|
||||
+10
-6
@@ -203,9 +203,13 @@ func (o pdfObjectOrigin) merge(other pdfObjectOrigin) pdfObjectOrigin {
|
||||
}
|
||||
|
||||
// ImportPDF 将PDF内容转换为独立OFD编辑文档,失败时不返回部分结果
|
||||
// 入参: ctx 取消上下文, source PDF数据, size 字节数, options 转换选项
|
||||
// 返回的编辑文档独立于输入源,函数返回后可关闭输入源
|
||||
// 入参: ctx 取消上下文, input 输入源, options 转换选项
|
||||
// 返回: *Editor 编辑文档, PDFImportReport 转换统计, error 错误信息
|
||||
func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFImportOptions) (*Editor, PDFImportReport, error) {
|
||||
func ImportPDF(ctx context.Context, input *Input, options PDFImportOptions) (*Editor, PDFImportReport, error) {
|
||||
if input == nil {
|
||||
return nil, PDFImportReport{}, fmt.Errorf("missing input")
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, PDFImportReport{}, err
|
||||
}
|
||||
@@ -241,7 +245,7 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
||||
readWarning = func(diagnostic pdfgo.Diagnostic) { diagnostics = append(diagnostics, diagnostic) }
|
||||
readOptions.Warning = func(diagnostic pdfgo.Diagnostic) { readWarning(diagnostic) }
|
||||
}
|
||||
reader, err := pdfgo.NewReaderWithOptions(convertReader{context: ctx, source: source}, size, readOptions)
|
||||
reader, err := pdfgo.NewReaderWithOptions(convertReader{ctx: ctx, input: input}, input.Size(), readOptions)
|
||||
if err != nil {
|
||||
return nil, PDFImportReport{}, err
|
||||
}
|
||||
@@ -370,10 +374,10 @@ func ImportPDF(ctx context.Context, source io.ReaderAt, size int64, options PDFI
|
||||
}
|
||||
|
||||
// ConvertPDF 将PDF转换并写入OFD,转换阶段失败时不写入目标
|
||||
// 入参: ctx 取消上下文, source PDF数据, size 字节数, output OFD输出, options 转换选项
|
||||
// 入参: ctx 取消上下文, input 输入源, output OFD输出, options 转换选项
|
||||
// 返回: PDFImportReport 转换统计, error 错误信息
|
||||
func ConvertPDF(ctx context.Context, source io.ReaderAt, size int64, output io.Writer, options PDFImportOptions) (PDFImportReport, error) {
|
||||
editor, report, err := ImportPDF(ctx, source, size, options)
|
||||
func ConvertPDF(ctx context.Context, input *Input, output io.Writer, options PDFImportOptions) (PDFImportReport, error) {
|
||||
editor, report, err := ImportPDF(ctx, input, options)
|
||||
if err != nil {
|
||||
return PDFImportReport{}, err
|
||||
}
|
||||
|
||||
+9
-9
@@ -140,7 +140,7 @@ func (r *Reader) DocumentPageCountContext(ctx context.Context, index int) (int,
|
||||
return 0, err
|
||||
}
|
||||
var doc documentPageList
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(&doc); err != nil {
|
||||
return 0, fmt.Errorf("failed to unmarshal document.xml: %w", err)
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func (r *Reader) docStructureContext(ctx context.Context) (*Document, error) {
|
||||
return nil, err
|
||||
}
|
||||
var doc Document
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(&doc); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal document.xml: %w", err)
|
||||
}
|
||||
@@ -581,7 +581,7 @@ func (r *Reader) OpenAttachmentContext(ctx context.Context, id string) (io.ReadC
|
||||
stream.Close()
|
||||
return nil, err
|
||||
}
|
||||
return imageInput{ReadCloser: stream, context: ctx}, nil
|
||||
return imageInput{ReadCloser: stream, ctx: ctx}, nil
|
||||
}
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
@@ -733,7 +733,7 @@ func (r *Reader) initRootContext(ctx context.Context) error {
|
||||
return fmt.Errorf("failed to read ofd.xml: %w", err)
|
||||
}
|
||||
var ofd OFD
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(&ofd); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal ofd.xml: %w", err)
|
||||
}
|
||||
@@ -856,7 +856,7 @@ func (r *Reader) readFileViewContext(ctx context.Context, name string) ([]byte,
|
||||
return nil, err
|
||||
}
|
||||
defer input.Close()
|
||||
data, err := io.ReadAll(imageInput{ReadCloser: input, context: ctx})
|
||||
data, err := io.ReadAll(imageInput{ReadCloser: input, ctx: ctx})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -949,7 +949,7 @@ func (r *Reader) loadResContext(ctx context.Context, resPath string) error {
|
||||
return ctx.Err()
|
||||
}
|
||||
var res Res
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(&res); err != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
@@ -1059,7 +1059,7 @@ func (r *Reader) readPageContent(ctx context.Context, page Page, textOnly, templ
|
||||
return nil, err
|
||||
}
|
||||
var content PageContent
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if textOnly {
|
||||
err = xml.NewTokenDecoder(&textPageTokens{Decoder: decoder}).Decode(&content)
|
||||
} else {
|
||||
@@ -1123,7 +1123,7 @@ func (r *Reader) readPageHeaderContext(ctx context.Context, page Page) (PageCont
|
||||
}
|
||||
defer f.Close()
|
||||
var content PageContent
|
||||
d := xml.NewDecoder(imageInput{ReadCloser: f, context: ctx})
|
||||
d := xml.NewDecoder(imageInput{ReadCloser: f, ctx: ctx})
|
||||
var root xml.Name
|
||||
scan:
|
||||
for {
|
||||
@@ -1239,7 +1239,7 @@ func (r *Reader) readDocumentPartContext(ctx context.Context, loc string, value
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
if err := decoder.Decode(value); err != nil {
|
||||
return "", fmt.Errorf("failed to unmarshal %s: %w", path.Base(fullPath), err)
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ func (r *Reader) scanDocumentFileReferences(ctx context.Context, name string, re
|
||||
return err
|
||||
}
|
||||
defer input.Close()
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: input, context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: input, ctx: ctx})
|
||||
var stack []xml.Name
|
||||
var positions []*editorXML
|
||||
root, base := "", ""
|
||||
|
||||
@@ -161,7 +161,7 @@ func (c *semanticCompiler) drawStampContent(stamp Stamp) error {
|
||||
return err
|
||||
}
|
||||
if stamp.Type == "ofd" {
|
||||
reader, err := NewReader(bytes.NewReader(stamp.Data), int64(len(stamp.Data)))
|
||||
reader, err := NewReader(&Input{reader: bytes.NewReader(stamp.Data), size: int64(len(stamp.Data))})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (r *Renderer) RenderToZIP(writer io.Writer, format string, indices ...int)
|
||||
return err
|
||||
}
|
||||
var countWritten int64
|
||||
archive := zip.NewWriter(convertWriter{context: ctx, writer: writer, count: &countWritten})
|
||||
archive := zip.NewWriter(convertWriter{ctx: ctx, writer: writer, count: &countWritten})
|
||||
configureArchive(archive, r.Compression)
|
||||
method := zip.Deflate
|
||||
if extension == "png" || extension == "jpg" {
|
||||
@@ -176,7 +176,7 @@ func (r *Renderer) RenderToText(page *PageContent, writer io.Writer) error {
|
||||
return err
|
||||
}
|
||||
var written int64
|
||||
writer = convertWriter{context: ctx, writer: writer, count: &written}
|
||||
writer = convertWriter{ctx: ctx, writer: writer, count: &written}
|
||||
_, err = text.WriteTo(writer)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -207,7 +207,7 @@ func (r *Renderer) RenderToMultiPageText(writer io.Writer, indices ...int) error
|
||||
return err
|
||||
}
|
||||
var count int64
|
||||
writer = convertWriter{context: ctx, writer: writer, count: &count}
|
||||
writer = convertWriter{ctx: ctx, writer: writer, count: &count}
|
||||
written := false
|
||||
for i, index := range indices {
|
||||
text, err := r.PageTextByIndexContext(ctx, index)
|
||||
@@ -248,7 +248,7 @@ func (r *Renderer) RenderToPNG(page *PageContent, writer io.Writer) error {
|
||||
return err
|
||||
}
|
||||
var written int64
|
||||
writer = convertWriter{context: r.outputContext(), writer: writer, count: &written}
|
||||
writer = convertWriter{ctx: r.outputContext(), writer: writer, count: &written}
|
||||
img, err := r.RenderToImage(page)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -277,7 +277,7 @@ func (r *Renderer) RenderToJPEG(page *PageContent, writer io.Writer, options *jp
|
||||
return err
|
||||
}
|
||||
var written int64
|
||||
writer = convertWriter{context: r.outputContext(), writer: writer, count: &written}
|
||||
writer = convertWriter{ctx: r.outputContext(), writer: writer, count: &written}
|
||||
img, err := r.RenderToImage(page)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -340,7 +340,7 @@ func (r *Renderer) RenderToEPS(page *PageContent, writer io.Writer) error {
|
||||
return fmt.Errorf("eps: %w", ErrBackendUnavailable)
|
||||
}
|
||||
var written int64
|
||||
writer = convertWriter{context: r.outputContext(), writer: writer, count: &written}
|
||||
writer = convertWriter{ctx: r.outputContext(), writer: writer, count: &written}
|
||||
return r.backends.EPS.RenderEPS(r, page, writer)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -73,6 +73,6 @@ func (r *Renderer) renderSVG(page *PageContent, writer io.Writer, mode SVGMode)
|
||||
return SVGResources{}, fmt.Errorf("svg: %w", ErrBackendUnavailable)
|
||||
}
|
||||
var written int64
|
||||
writer = convertWriter{context: r.outputContext(), writer: writer, count: &written}
|
||||
writer = convertWriter{ctx: r.outputContext(), writer: writer, count: &written}
|
||||
return r.backends.SVG.RenderSVG(r, page, writer, mode)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
// canvasPageVisitor 将公共页面遍历结果交给Canvas解释器
|
||||
type canvasPageVisitor struct {
|
||||
renderer *Renderer
|
||||
context *canvas.Context
|
||||
ctx *canvas.Context
|
||||
height float64
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ func (v *canvasPageVisitor) DrawObject(object *GraphicObject, state RenderState)
|
||||
}
|
||||
switch object.Type {
|
||||
case "TextObject":
|
||||
v.renderer.renderText(v.context, object.TextObject, v.height, state.Defaults, state.Parent, state.BoundaryInCTM, clip)
|
||||
v.renderer.renderText(v.ctx, object.TextObject, v.height, state.Defaults, state.Parent, state.BoundaryInCTM, clip)
|
||||
case "PathObject":
|
||||
v.renderer.renderPath(v.context, object.PathObject, v.height, state.Defaults, state.Parent, state.BoundaryInCTM, clip)
|
||||
v.renderer.renderPath(v.ctx, object.PathObject, v.height, state.Defaults, state.Parent, state.BoundaryInCTM, clip)
|
||||
case "ImageObject":
|
||||
v.renderer.renderImage(v.context, object.ImageObject, v.height, state.Parent, state.BoundaryInCTM, clip)
|
||||
v.renderer.renderImage(v.ctx, object.ImageObject, v.height, state.Parent, state.BoundaryInCTM, clip)
|
||||
}
|
||||
return v.renderer.renderError
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func (v *canvasPageVisitor) DrawObject(object *GraphicObject, state RenderState)
|
||||
// 入参: stamp 签章
|
||||
// 返回: error 绘制错误
|
||||
func (v *canvasPageVisitor) DrawStamp(stamp Stamp) error {
|
||||
v.renderer.renderStamp(v.context, stamp, v.height)
|
||||
v.renderer.renderStamp(v.ctx, stamp, v.height)
|
||||
return v.renderer.renderError
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (v *canvasPageVisitor) DrawStamp(stamp Stamp) error {
|
||||
// 入参: object 源对象
|
||||
// 返回: bool 是否建立分组
|
||||
func (v *canvasPageVisitor) BeginObject(object *GraphicObject) bool {
|
||||
groups, ok := v.context.Renderer.(interface {
|
||||
groups, ok := v.ctx.Renderer.(interface {
|
||||
beginObject(*GraphicObject) bool
|
||||
endObject()
|
||||
})
|
||||
@@ -130,7 +130,7 @@ func (v *canvasPageVisitor) BeginObject(object *GraphicObject) bool {
|
||||
// 入参: id 注解标识
|
||||
// 返回: bool 是否建立分组
|
||||
func (v *canvasPageVisitor) BeginAnnotation(id string) bool {
|
||||
groups, ok := v.context.Renderer.(interface {
|
||||
groups, ok := v.ctx.Renderer.(interface {
|
||||
beginAnnotation(string) bool
|
||||
endObject()
|
||||
})
|
||||
@@ -139,13 +139,13 @@ func (v *canvasPageVisitor) BeginAnnotation(id string) bool {
|
||||
|
||||
// EndObject 结束对象或注解分组
|
||||
func (v *canvasPageVisitor) EndObject() {
|
||||
v.context.Renderer.(interface{ endObject() }).endObject()
|
||||
v.ctx.Renderer.(interface{ endObject() }).endObject()
|
||||
}
|
||||
|
||||
// SkipObjects 保留不可见对象的编号
|
||||
// 入参: count 对象数量
|
||||
func (v *canvasPageVisitor) SkipObjects(count int) {
|
||||
if groups, ok := v.context.Renderer.(interface{ skipObjects(int) }); ok {
|
||||
if groups, ok := v.ctx.Renderer.(interface{ skipObjects(int) }); ok {
|
||||
groups.skipObjects(count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ func (n *pdfNavigation) writeAttachments(ctx context.Context, options *pdfgo.Rew
|
||||
if err != nil {
|
||||
return fmt.Errorf("read attachment %s: %w", id, err)
|
||||
}
|
||||
data, err := io.ReadAll(imageInput{ReadCloser: input, context: ctx})
|
||||
data, err := io.ReadAll(imageInput{ReadCloser: input, ctx: ctx})
|
||||
closeErr := input.Close()
|
||||
if err != nil {
|
||||
return fmt.Errorf("read attachment %s: %w", id, err)
|
||||
|
||||
@@ -78,7 +78,7 @@ func (n *pdfNavigation) loadMediaFiles(ctx context.Context, files map[string]*pd
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := io.ReadAll(imageInput{ReadCloser: input, context: ctx})
|
||||
data, err := io.ReadAll(imageInput{ReadCloser: input, ctx: ctx})
|
||||
closeErr := input.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -49,7 +49,7 @@ func (r *Renderer) renderStamp(ctx *canvas.Context, s Stamp, pageH float64) {
|
||||
}
|
||||
return
|
||||
}
|
||||
reader, err := NewReader(bytes.NewReader(s.Data), int64(len(s.Data)))
|
||||
reader, err := NewReader(&Input{reader: bytes.NewReader(s.Data), size: int64(len(s.Data))})
|
||||
if err == nil {
|
||||
defer reader.Close()
|
||||
doc, err := reader.DocContext(pageContext)
|
||||
@@ -114,7 +114,7 @@ func (r *Renderer) renderOFDStampImage(data []byte) image.Image {
|
||||
r.renderError = err
|
||||
return nil
|
||||
}
|
||||
reader, err := NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
reader, err := NewReader(&Input{reader: bytes.NewReader(data), size: int64(len(data))})
|
||||
if err != nil {
|
||||
r.renderError = err
|
||||
return nil
|
||||
|
||||
@@ -106,7 +106,7 @@ func (s *svgResourceRenderer) RenderImage(img image.Image, m canvas.Matrix) {
|
||||
size := img.Bounds().Size()
|
||||
m = m.Translate(0, float64(size.Y))
|
||||
matrix := Matrix{a: m[0][0], b: -m[1][0], c: -m[0][1], d: m[1][1], e: m[0][2], f: height - m[1][2]}
|
||||
writer := imageOutput{context: ctx, writer: s.writer}
|
||||
writer := imageOutput{ctx: ctx, writer: s.writer}
|
||||
if _, s.err = fmt.Fprintf(writer, `<image transform="matrix(%s)" width="%d" height="%d" xlink:href="`, matrix.String(), size.X, size.Y); s.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ func (r *Reader) OpenResource(ctx context.Context, kind, id string) (io.ReadClos
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return imageInput{ReadCloser: stream, context: ctx}, nil
|
||||
return imageInput{ReadCloser: stream, ctx: ctx}, nil
|
||||
}
|
||||
|
||||
// resourceList 缓存当前阅读视图的资源摘要,不保留扫描使用的XML树
|
||||
|
||||
+2
-2
@@ -186,7 +186,7 @@ func (r *Reader) parseSignatures(ctx context.Context, doc *Document) error {
|
||||
}
|
||||
defer f.Close()
|
||||
var signatures Signatures
|
||||
if err := xml.NewDecoder(imageInput{ReadCloser: f, context: ctx}).Decode(&signatures); err != nil {
|
||||
if err := xml.NewDecoder(imageInput{ReadCloser: f, ctx: ctx}).Decode(&signatures); err != nil {
|
||||
return err
|
||||
}
|
||||
pages := signaturePageNumbers(doc)
|
||||
@@ -202,7 +202,7 @@ func (r *Reader) parseSignatures(ctx context.Context, doc *Document) error {
|
||||
}
|
||||
defer sf.Close()
|
||||
var sigFile SignatureFile
|
||||
if err := xml.NewDecoder(imageInput{ReadCloser: sf, context: ctx}).Decode(&sigFile); err != nil {
|
||||
if err := xml.NewDecoder(imageInput{ReadCloser: sf, ctx: ctx}).Decode(&sigFile); err != nil {
|
||||
return
|
||||
}
|
||||
var sealType string
|
||||
|
||||
+4
-22
@@ -235,29 +235,11 @@ func WithSignatureVerifyTime(t time.Time) SignatureVerifyOption {
|
||||
}
|
||||
}
|
||||
|
||||
// VerifySignaturesBytes 验证OFD字节数据签名
|
||||
// 入参: data OFD字节数据, opts 签名验证选项
|
||||
// VerifySignatures 验证输入源中的OFD签名,不接管输入源生命周期
|
||||
// 入参: input 输入源, opts 签名验证选项
|
||||
// 返回: []SignatureVerifyReport 签名验证报告, error 错误信息
|
||||
func VerifySignaturesBytes(data []byte, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) {
|
||||
return VerifySignaturesReader(bytes.NewReader(data), int64(len(data)), opts...)
|
||||
}
|
||||
|
||||
// VerifySignaturesStream 验证OFD顺序流签名
|
||||
// 入参: r IO顺序读取器, opts 签名验证选项
|
||||
// 返回: []SignatureVerifyReport 签名验证报告, error 错误信息
|
||||
func VerifySignaturesStream(r io.Reader, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) {
|
||||
data, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return VerifySignaturesBytes(data, opts...)
|
||||
}
|
||||
|
||||
// VerifySignaturesReader 验证OFD读取器签名
|
||||
// 入参: r IO读取器, size 数据大小, opts 签名验证选项
|
||||
// 返回: []SignatureVerifyReport 签名验证报告, error 错误信息
|
||||
func VerifySignaturesReader(r io.ReaderAt, size int64, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) {
|
||||
reader, err := NewReader(r, size)
|
||||
func VerifySignatures(input *Input, opts ...SignatureVerifyOption) ([]SignatureVerifyReport, error) {
|
||||
reader, err := NewReader(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+4
-4
@@ -84,7 +84,7 @@ type signatureIDSequence uint64
|
||||
// 入参: data 最终包字节, options 签署选项
|
||||
// 返回: []byte 签署后的完整包, error 错误信息
|
||||
func SignPackage(data []byte, options SignatureWriteOptions) ([]byte, error) {
|
||||
r, err := NewReader(bytes.NewReader(data), int64(len(data)), options.ReaderOptions...)
|
||||
r, err := NewReader(&Input{reader: bytes.NewReader(data), size: int64(len(data))}, options.ReaderOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func SignEditorTo(writer io.Writer, editor *Editor, options SignatureWriteOption
|
||||
if _, err := editor.writePlaintext(&plain); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
r, err := NewReader(bytes.NewReader(plain.Bytes()), int64(plain.Len()))
|
||||
r, err := NewReader(&Input{reader: bytes.NewReader(plain.Bytes()), size: int64(plain.Len())})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -192,7 +192,7 @@ func signatureWriteOutput(r *Reader, options SignatureWriteOptions) ([]byte, err
|
||||
if encryption != nil {
|
||||
defer clear(data)
|
||||
if options.progress != nil {
|
||||
signed, err := NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
signed, err := NewReader(&Input{reader: bytes.NewReader(data), size: int64(len(data))})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -434,7 +434,7 @@ func signatureWritePackage(parts map[string][]byte, options SignatureWriteOption
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
check, err := NewReader(bytes.NewReader(output), int64(len(output)))
|
||||
check, err := NewReader(&Input{reader: bytes.NewReader(output), size: int64(len(output))})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+1
-1
@@ -133,7 +133,7 @@ func (r *Reader) readStructureContext(ctx context.Context, tag CustomTag) (Struc
|
||||
// 返回: *StructureNode 根节点, error 解析、限额或取消错误
|
||||
func parseStructure(ctx context.Context, data []byte, tag CustomTag) (*StructureNode, error) {
|
||||
data = bytes.TrimPrefix(data, []byte{0xef, 0xbb, 0xbf})
|
||||
d := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), context: ctx})
|
||||
d := xml.NewDecoder(imageInput{ReadCloser: io.NopCloser(bytes.NewReader(data)), ctx: ctx})
|
||||
var root *StructureNode
|
||||
var stack []*StructureNode
|
||||
var text []*strings.Builder
|
||||
|
||||
+1
-1
@@ -180,7 +180,7 @@ func (r *Reader) readDocumentVersionContext(ctx context.Context, id, location st
|
||||
return DocumentVersionInfo{}, err
|
||||
}
|
||||
defer input.Close()
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: input, context: ctx})
|
||||
decoder := xml.NewDecoder(imageInput{ReadCloser: input, ctx: ctx})
|
||||
var info DocumentVersionInfo
|
||||
var root, parent *editorXML
|
||||
var files, entry bool
|
||||
|
||||
@@ -293,7 +293,7 @@ func (r *Reader) versionFileUnchanged(ctx context.Context, name string, data []b
|
||||
if len(*buffer) == 0 {
|
||||
*buffer = make([]byte, 32*1024)
|
||||
}
|
||||
reader := imageInput{ReadCloser: input, context: ctx}
|
||||
reader := imageInput{ReadCloser: input, ctx: ctx}
|
||||
for len(data) != 0 {
|
||||
block := (*buffer)[:min(len(*buffer), len(data))]
|
||||
if _, err := io.ReadFull(&reader, block); err != nil {
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ func (e *Editor) Snapshot(ctx context.Context) (*Editor, error) {
|
||||
if reader.Zip != nil {
|
||||
var data bytes.Buffer
|
||||
var count int64
|
||||
archive := zip.NewWriter(convertWriter{context: ctx, writer: &data, count: &count})
|
||||
archive := zip.NewWriter(convertWriter{ctx: ctx, writer: &data, count: &count})
|
||||
for _, file := range reader.Zip.File {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return nil, err
|
||||
|
||||
Reference in new issue
Block a user