diff --git a/assets/webui/ofdgo.js b/assets/webui/ofdgo.js index 936913d..f171465 100644 --- a/assets/webui/ofdgo.js +++ b/assets/webui/ofdgo.js @@ -3618,7 +3618,7 @@ async function openOFD(file, remote = null) { await openDocument({ doc, pageIndex: remote?.pageIndex || 0, resetScroll: true, openSeq }); } catch (err) { if (openSeq === state.openSeq) { - if (err.name === "AbortError") setStatus("转换已取消"); + if (err.name === "AbortError") setStatus("打开已取消"); else showError(err, !state.doc); setBusy(false); } diff --git a/go.mod b/go.mod index 1409ae1..5a0ed84 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/tdewolff/canvas v0.0.0-20260923214215-09804640d00c github.com/tdewolff/font v0.0.0-20260913163313-54f98bb59ee6 github.com/xiaoqidun/jbig2 v1.1.1 - github.com/xiaoqidun/pdfgo v0.0.0-20260929145256-b04a96874771 + github.com/xiaoqidun/pdfgo v0.0.0-20260929154445-b9e68e6f6960 golang.org/x/crypto v0.57.0 golang.org/x/image v0.46.0 golang.org/x/text v0.42.0 diff --git a/go.sum b/go.sum index 3f5148e..3e527d2 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk= github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8= github.com/xiaoqidun/jbig2 v1.1.1 h1:SjliS9ZO0e9HX5oYUfBuXrfFsXhWricrSz+XY4zORDI= github.com/xiaoqidun/jbig2 v1.1.1/go.mod h1:8QonVWtENmh2kc6l+leqeGl7psb79DWlHDkCpdChjfQ= -github.com/xiaoqidun/pdfgo v0.0.0-20260929145256-b04a96874771 h1:qddSlHkgZhRMyawpOS3TlW5nu8+IurZFjxS4Kwg8YLA= -github.com/xiaoqidun/pdfgo v0.0.0-20260929145256-b04a96874771/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ= +github.com/xiaoqidun/pdfgo v0.0.0-20260929154445-b9e68e6f6960 h1:msQWyUBuNUnvJ+VKrUELivQPYFxSVanp/iSncN1c+yA= +github.com/xiaoqidun/pdfgo v0.0.0-20260929154445-b9e68e6f6960/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.8.5 h1:r6N5afV5qj/5S4UTch8agZHJ8UxNCMwX7WjkkJam2NA= diff --git a/internal/webui/wasm.go b/internal/webui/wasm.go index 9589b35..d43df4a 100644 --- a/internal/webui/wasm.go +++ b/internal/webui/wasm.go @@ -545,7 +545,12 @@ func packFiles(args []js.Value) (any, error) { source := &browserReaderAt{read: read, checkpoint: args[4], size: int64(file.Get("size").Float())} entries[i] = ofdgo.ArchiveFile{Name: name, Source: source, Size: source.size} } - options := ofdgo.ArchiveOptions{OnProgress: func(completed, total int) error { return awaitExport(args[3], completed, total) }} + options := ofdgo.ArchiveOptions{OnProgress: func(completed, total int) error { + if completed > 0 { + entries[completed-1].Source.(*browserReaderAt).data = nil + } + return awaitExport(args[3], completed, total) + }} if len(args) > 5 && !args[5].IsUndefined() && !args[5].IsNull() { if err := compressionFromJS(args[5], &options.Compression); err != nil { return nil, err @@ -1422,7 +1427,6 @@ func writeAnnotation(args []js.Value) (any, error) { var ids []string result, err := changeObjects(func() error { font, resource := "", "" - var fontData []byte if !args[2].IsNull() { data, err := bytesFromJS(args[2]) if err != nil { @@ -1432,7 +1436,6 @@ func writeAnnotation(args []js.Value) (any, error) { if err != nil { return err } - fontData = data } if !args[3].IsNull() { data, err := bytesFromJS(args[3]) @@ -1475,11 +1478,31 @@ func writeAnnotation(args []js.Value) (any, error) { if err != nil { return err } - var watermarkBounds ofdgo.Box if options.Kind == "watermark" { - if area := options.Area; area != nil && (area.X < 0 || area.Y < 0 || area.W < 0 || area.H < 0 || area.X+area.W > 1 || area.Y+area.H > 1 || options.Tile && (area.W == 0 || area.H == 0)) { - return fmt.Errorf("invalid watermark area") + boundary := fmt.Sprintf("0 0 %g %g", options.Width, options.Height) + var object ofdgo.GraphicObject + if resource != "" { + object = ofdgo.GraphicObject{Type: "ImageObject", ImageObject: ofdgo.ImageObject{ResourceID: resource, Boundary: boundary, Alpha: &options.Alpha}} + } else { + text := ofdgo.TextObject{Font: font, Size: options.Size, Boundary: boundary, Alpha: &options.Alpha} + if err := currentEditor.LayoutText(&text, options.Text, ofdgo.TextLayout{Align: "center"}); err != nil { + return err + } + if err := setTextColor(&text, options.Color); err != nil { + return err + } + object = ofdgo.GraphicObject{Type: "TextObject", TextObject: text} } + added, err := currentEditor.AddWatermarks(context.Background(), pages, object, ofdgo.WatermarkOptions{Creator: options.Creator, Angle: options.Angle, Tile: options.Tile, Area: options.Area}) + if err != nil { + return err + } + for i, index := range pages { + if index == page { + ids = append(ids, "annotation:"+added[i]) + } + } + return nil } for _, index := range pages { box := ofdgo.Box{X: options.X, Y: options.Y, W: options.Width, H: options.Height} @@ -1491,56 +1514,20 @@ func writeAnnotation(args []js.Value) (any, error) { switch options.Kind { case "note": annotation.Type, annotation.Remark = "Path", options.Text - case "watermark", "stamp": - annotation.Type = "Watermark" - if options.Kind == "stamp" { - annotation.Type = "Stamp" - } - var object ofdgo.GraphicObject - if resource != "" { - object = ofdgo.GraphicObject{Type: "ImageObject", ImageObject: ofdgo.ImageObject{ResourceID: resource, Boundary: fmt.Sprintf("0 0 %g %g", box.W, box.H), CTM: fmt.Sprintf("%g 0 0 %g 0 0", box.W, box.H), Alpha: &options.Alpha}} - } else { - if options.Kind == "stamp" { - return fmt.Errorf("stamp requires an image") - } - text := ofdgo.TextObject{Font: font, Size: options.Size, Boundary: fmt.Sprintf("0 0 %g %g", box.W, box.H), Alpha: &options.Alpha} - if err := currentEditor.LayoutText(&text, options.Text, ofdgo.TextLayout{Align: "center"}); err != nil { - return err - } - if err := setTextColor(&text, options.Color); err != nil { - return err - } - object = ofdgo.GraphicObject{Type: "TextObject", TextObject: text} + case "stamp": + annotation.Type = "Stamp" + if resource == "" { + return fmt.Errorf("stamp requires an image") } + object := ofdgo.GraphicObject{Type: "ImageObject", ImageObject: ofdgo.ImageObject{ResourceID: resource, Boundary: fmt.Sprintf("0 0 %g %g", box.W, box.H), CTM: fmt.Sprintf("%g 0 0 %g 0 0", box.W, box.H), Alpha: &options.Alpha}} angle := options.Angle * math.Pi / 180 matrix := ofdgo.NewMatrix(fmt.Sprintf("%g %g %g %g 0 0", math.Cos(angle), math.Sin(angle), -math.Sin(angle), math.Cos(angle))) matrix = ofdgo.TranslationMatrix(box.W/2, box.H/2).Multiply(matrix).Multiply(ofdgo.TranslationMatrix(-box.W/2, -box.H/2)) - if object.Type == "TextObject" { - object.TextObject.CTM = matrix.String() - } else { - object.ImageObject.CTM = matrix.Multiply(ofdgo.NewMatrix(object.ImageObject.CTM)).String() - } + object.ImageObject.CTM = matrix.Multiply(ofdgo.NewMatrix(object.ImageObject.CTM)).String() annotation.Appearance.Objects = []ofdgo.GraphicObject{object} default: return fmt.Errorf("unsupported annotation kind %q", options.Kind) } - if options.Kind == "watermark" { - if watermarkBounds.W == 0 { - watermarkBounds, err = measureWatermark(annotation.Appearance.Objects[0], fontData, currentEditor.Backends()) - if err != nil { - return err - } - } - content, err := currentSession.pageContent(index) - if err != nil { - return err - } - page, err := currentSession.pageBox(index, content) - if err != nil { - return err - } - layoutWatermark(&annotation, watermarkBounds, options, page) - } id, err = currentEditor.AddAnnotation(index, annotation) } if err != nil { @@ -1558,94 +1545,6 @@ func writeAnnotation(args []js.Value) (any, error) { return editorSelectionInfo{editorInfo: result.(editorInfo), SelectedIDs: ids}, nil } -// measureWatermark 复用标准渲染度量水印,临时文档保留尚未提交的字体引用 -// 入参: object 水印图元, fontData 字体字节, backends 当前后端配置 -// 返回: ofdgo.Box 实际绘制范围, error 错误信息 -func measureWatermark(object ofdgo.GraphicObject, fontData []byte, backends ofdgo.RenderBackends) (ofdgo.Box, error) { - if object.Type == "ImageObject" { - return ofdgo.NewMatrix(object.ImageObject.CTM).TransformBox(ofdgo.Box{W: 1, H: 1}), nil - } - editor := ofdgo.NewEditor() - if _, err := editor.AddPage(210, 297); err != nil { - return ofdgo.Box{}, err - } - font, err := editor.AddFont(FontFile{Data: fontData}, 0) - if err != nil { - return ofdgo.Box{}, err - } - object.TextObject.Font, object.TextObject.Alpha = font, nil - if _, err := editor.AddObject(0, object); err != nil { - return ofdgo.Box{}, err - } - reader, err := editor.Reader() - if err != nil { - return ofdgo.Box{}, err - } - defer reader.Close() - if _, err := reader.PageContentByIndex(0); err != nil { - return ofdgo.Box{}, err - } - bounds, err := ofdgo.NewRenderer(reader, ofdgo.WithRenderBackends(backends)).ObjectBounds(ofdgo.GraphicObject{Type: "CompositeObject", CompositeGraphicUnit: ofdgo.CompositeGraphicUnit{ - Boundary: object.TextObject.Boundary, Objects: []ofdgo.GraphicObject{object}, - }}, "") - if err != nil { - return ofdgo.Box{}, err - } - if bounds.W <= 0 || bounds.H <= 0 { - return ofdgo.Box{}, fmt.Errorf("watermark has no visible content") - } - return bounds, nil -} - -// layoutWatermark 按实际绘制边界居中水印或均匀平铺,选区按页面比例映射 -// 入参: annotation 水印注解, bounds 绘制范围, options 输入选项, page 页面范围 -func layoutWatermark(annotation *ofdgo.Annotation, bounds ofdgo.Box, options annotationOptions, page ofdgo.Box) { - base := annotation.Appearance.Objects[0] - area := ofdgo.Box{W: page.W, H: page.H} - if options.Area != nil { - area = ofdgo.Box{X: options.Area.X * page.W, Y: options.Area.Y * page.H, W: options.Area.W * page.W, H: options.Area.H * page.H} - } - scale := 1.0 - if area.W > 0 && area.H > 0 { - scale = math.Min(1, math.Min(area.W/bounds.W, area.H/bounds.H)) - } - if scale != 1 { - matrix := ofdgo.NewMatrix(fmt.Sprintf("%g 0 0 %g 0 0", scale, scale)) - if base.Type == "TextObject" { - base.TextObject.CTM = matrix.Multiply(ofdgo.NewMatrix(base.TextObject.CTM)).String() - } else { - base.ImageObject.CTM = matrix.Multiply(ofdgo.NewMatrix(base.ImageObject.CTM)).String() - } - bounds = matrix.TransformBox(bounds) - } - columns, rows := 1, 1 - if options.Tile { - angle := options.Angle * math.Pi / 180 - width := math.Max(bounds.W, (math.Abs(math.Cos(angle))*options.Width+math.Abs(math.Sin(angle))*options.Height)*scale) - height := math.Max(bounds.H, (math.Abs(math.Sin(angle))*options.Width+math.Abs(math.Cos(angle))*options.Height)*scale) - columns = max(1, int((area.W+10)/(width+10))) - rows = max(1, int((area.H+10)/(height+10))) - } else { - area = ofdgo.Box{X: area.X + (area.W-bounds.W)/2, Y: area.Y + (area.H-bounds.H)/2, W: bounds.W, H: bounds.H} - } - annotation.Appearance.Boundary = fmt.Sprintf("%g %g %g %g", area.X, area.Y, area.W, area.H) - annotation.Appearance.Objects = make([]ofdgo.GraphicObject, 0, columns*rows) - for row := range rows { - for column := range columns { - x := (float64(column)+0.5)*area.W/float64(columns) - bounds.X - bounds.W/2 - y := (float64(row)+0.5)*area.H/float64(rows) - bounds.Y - bounds.H/2 - object := base - boundary := fmt.Sprintf("%g %g %g %g", x, y, options.Width, options.Height) - if object.Type == "TextObject" { - object.TextObject.Boundary = boundary - } else { - object.ImageObject.Boundary = boundary - } - annotation.Appearance.Objects = append(annotation.Appearance.Objects, object) - } - } -} - // insertInk 将一笔采样写为一个标准矢量对象,复用内部插入与撤销逻辑 // 入参: args 页面、JSON采样、线宽、压感开关、颜色及可选父路径 // 返回: any 文档和选区, error 错误信息 @@ -4473,6 +4372,9 @@ func saveDocument(args []js.Value) (any, error) { // 入参: editor 待写出文档, args 写出回调、进度回调及可选页面索引、压缩配置、取消检查 // 返回: any 保存结果, error 错误信息 func saveEditor(editor *ofdgo.Editor, args []js.Value) (any, error) { + if len(args) == 0 || args[0].Type() != js.TypeFunction { + return nil, fmt.Errorf("missing output callback") + } if len(args) > 1 { editor.OnWriteProgress = editorOperationProgress(args[1]) defer func() { editor.OnWriteProgress = nil }() diff --git a/ofdgo_compression_resources.go b/ofdgo_compression_resources.go index e8fb5a1..9f97fce 100644 --- a/ofdgo_compression_resources.go +++ b/ofdgo_compression_resources.go @@ -78,8 +78,11 @@ func (e *Editor) compressResourceParts(parts map[string][]byte, reader *Reader, return err } } - resource, safe := refs.scan(input, name) + resource, safe := refs.scan(imageInput{ReadCloser: input, context: e.output.ctx}, name) input.Close() + if err := e.output.ctx.Err(); err != nil { + return err + } if !safe { return nil } diff --git a/ofdgo_editor_history.go b/ofdgo_editor_history.go index 391ba21..d4da115 100644 --- a/ofdgo_editor_history.go +++ b/ofdgo_editor_history.go @@ -37,8 +37,13 @@ func (e *Editor) Transaction(edit func(*Editor) error) error { next := e.transactionSnapshot() next.history, next.historyIndex, next.historyLimit = nil, 0, 0 *e = next + committed := false + defer func() { + if !committed { + *e = before + } + }() if err := edit(e); err != nil { - *e = before return err } changed := e.revision != before.revision @@ -54,6 +59,7 @@ func (e *Editor) Transaction(edit func(*Editor) error) error { change.redo = func(e *Editor) { e.restoreTransaction(after) } } } + committed = true return nil } diff --git a/ofdgo_editor_watermark.go b/ofdgo_editor_watermark.go new file mode 100644 index 0000000..1b1c456 --- /dev/null +++ b/ofdgo_editor_watermark.go @@ -0,0 +1,212 @@ +// Copyright 2025-2026 肖其顿 (XIAO QI DUN) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ofdgo + +import ( + "context" + "fmt" + "math" +) + +// WatermarkOptions 配置水印旋转、居中和平铺,Area为页面比例区域,nil使用整页 +// Angle为顺时针角度,Gap为平铺最小毫米间距,零值使用10毫米 +// 非平铺时Area宽高同时为零表示中心点定位,不缩放图元 +type WatermarkOptions struct { + Creator string + Angle float64 + Tile bool + Area *Box + Gap float64 +} + +// AddWatermarks 向指定页面添加文字或图片水印,复用编辑器字体来源及后端,一次操作计入一条撤销记录 +// 图元Boundary提供原始尺寸,忽略其位置;按实际绘制范围居中,超出区域时等比缩小 +// 失败或取消时保留原文档,输入图元不被修改 +// 入参: ctx 取消上下文, indexes 不重复的页面索引, object 水印图元, options 布局选项 +// 返回: []string 按页面顺序排列的注解标识, error 错误信息 +func (e *Editor) AddWatermarks(ctx context.Context, indexes []int, object GraphicObject, options WatermarkOptions) ([]string, error) { + if err := ctx.Err(); err != nil { + return nil, err + } + if err := e.validatePageIndexes(indexes); err != nil { + return nil, err + } + if !finite(options.Angle) || !finite(options.Gap) || options.Gap < 0 { + return nil, fmt.Errorf("invalid watermark angle or gap") + } + if area := options.Area; area != nil { + point := !options.Tile && area.W == 0 && area.H == 0 + if !finite(area.X) || !finite(area.Y) || !finite(area.W) || !finite(area.H) || area.X < 0 || area.Y < 0 || (!point && (area.W <= 0 || area.H <= 0)) || area.X+area.W > 1 || area.Y+area.H > 1 { + return nil, fmt.Errorf("invalid watermark area") + } + } + if options.Gap == 0 { + options.Gap = 10 + } + object = cloneEditorData(object) + var boundary, ctm *string + switch object.Type { + case "TextObject": + boundary, ctm = &object.TextObject.Boundary, &object.TextObject.CTM + case "ImageObject": + boundary, ctm = &object.ImageObject.Boundary, &object.ImageObject.CTM + default: + return nil, fmt.Errorf("watermark requires text or image") + } + box, err := creationBox(*boundary) + if err != nil || box.W <= 0 || box.H <= 0 { + return nil, fmt.Errorf("invalid watermark dimensions") + } + if len(indexes) == 0 { + return nil, nil + } + box.X, box.Y = 0, 0 + *boundary = editorBoxString(box) + angle := math.Mod(options.Angle, 360) * math.Pi / 180 + rotation := Matrix{a: math.Cos(angle), b: math.Sin(angle), c: -math.Sin(angle), d: math.Cos(angle)} + rotation = TranslationMatrix(box.W/2, box.H/2).Multiply(rotation).Multiply(TranslationMatrix(-box.W/2, -box.H/2)) + if *ctm != "" { + if _, err := creationNumbers(*ctm, 6); err != nil { + return nil, err + } + } else if object.Type == "ImageObject" { + *ctm = (Matrix{a: box.W, d: box.H}).String() + } + *ctm = rotation.Multiply(NewMatrix(*ctm)).String() + preview := e.transactionSnapshot() + preview.SetHistoryLimit(0) + id, err := preview.AddObject(indexes[0], object) + if err != nil { + return nil, err + } + object, err = preview.Object(indexes[0], id) + if err != nil { + return nil, err + } + reader, err := preview.Reader() + if err != nil { + return nil, err + } + defer reader.Close() + page, err := reader.PageContentByIndex(indexes[0]) + if err != nil { + return nil, err + } + renderer := e.newRenderer(reader) + measured := cloneEditorData(object) + if measured.Type == "TextObject" { + measured.TextObject.Alpha = nil + } else { + measured.ImageObject.Alpha = nil + } + bounds, err := renderer.ObjectBounds(measured, "") + if err != nil { + return nil, err + } + if !finite(bounds.X) || !finite(bounds.Y) || !finite(bounds.W) || !finite(bounds.H) || bounds.W <= 0 || bounds.H <= 0 { + return nil, fmt.Errorf("watermark has no visible content") + } + var ids []string + err = e.Transaction(func(edit *Editor) error { + for _, index := range indexes { + if err := ctx.Err(); err != nil { + return err + } + if index != indexes[0] { + page, err = reader.PageContentByIndex(index) + if err != nil { + return err + } + } + pageBox, err := renderer.GetPageBox(page) + if err != nil { + return err + } + annotation, err := layoutWatermark(ctx, object, bounds, box, options, pageBox) + if err != nil { + return err + } + id, err := edit.AddAnnotation(index, annotation) + if err != nil { + return err + } + ids = append(ids, id) + } + return ctx.Err() + }) + if err != nil { + return nil, err + } + return ids, nil +} + +// layoutWatermark 按绘制范围布局独立水印外观,保留页面原点及图元变换 +// 入参: ctx 取消上下文, base 水印图元, bounds 绘制范围, box 原始尺寸, options 布局选项, page 页面范围 +// 返回: Annotation 水印注解, error 取消或布局错误 +func layoutWatermark(ctx context.Context, base GraphicObject, bounds, box Box, options WatermarkOptions, page Box) (Annotation, error) { + area := page + if options.Area != nil { + area = Box{X: page.X + options.Area.X*page.W, Y: page.Y + options.Area.Y*page.H, W: options.Area.W * page.W, H: options.Area.H * page.H} + } + if !finite(page.X) || !finite(page.Y) || !finite(page.W) || !finite(page.H) || page.W <= 0 || page.H <= 0 { + return Annotation{}, fmt.Errorf("invalid watermark page area") + } + scale := 1.0 + if area.W > 0 && area.H > 0 { + scale = math.Min(1, math.Min(area.W/bounds.W, area.H/bounds.H)) + } + if scale != 1 { + matrix := Matrix{a: scale, d: scale} + if base.Type == "TextObject" { + base.TextObject.CTM = matrix.Multiply(NewMatrix(base.TextObject.CTM)).String() + } else { + base.ImageObject.CTM = matrix.Multiply(NewMatrix(base.ImageObject.CTM)).String() + } + bounds = matrix.TransformBox(bounds) + } + columns, rows := 1.0, 1.0 + if options.Tile { + angle := math.Mod(options.Angle, 360) * math.Pi / 180 + width := math.Max(bounds.W, (math.Abs(math.Cos(angle))*box.W+math.Abs(math.Sin(angle))*box.H)*scale) + height := math.Max(bounds.H, (math.Abs(math.Sin(angle))*box.W+math.Abs(math.Cos(angle))*box.H)*scale) + columns = math.Max(1, math.Floor((area.W+options.Gap)/(width+options.Gap))) + rows = math.Max(1, math.Floor((area.H+options.Gap)/(height+options.Gap))) + } else { + area = Box{X: area.X + (area.W-bounds.W)/2, Y: area.Y + (area.H-bounds.H)/2, W: bounds.W, H: bounds.H} + } + if !finite(columns*rows) || columns*rows > 100000 { + return Annotation{}, fmt.Errorf("too many watermark tiles") + } + annotation := Annotation{Type: "Watermark", Creator: options.Creator, Appearance: Appearance{Boundary: editorBoxString(area)}} + annotation.Appearance.Objects = make([]GraphicObject, 0, int(columns*rows)) + for row := 0; row < int(rows); row++ { + if err := ctx.Err(); err != nil { + return Annotation{}, err + } + for column := 0; column < int(columns); column++ { + x := (float64(column)+0.5)*area.W/columns - bounds.X - bounds.W/2 + y := (float64(row)+0.5)*area.H/rows - bounds.Y - bounds.H/2 + object := base + boundary := editorBoxString(Box{X: x, Y: y, W: box.W, H: box.H}) + if object.Type == "TextObject" { + object.TextObject.Boundary = boundary + } else { + object.ImageObject.Boundary = boundary + } + annotation.Appearance.Objects = append(annotation.Appearance.Objects, object) + } + } + return annotation, nil +} diff --git a/ofdgo_extract_image.go b/ofdgo_extract_image.go index d51b546..37dc7d1 100644 --- a/ofdgo_extract_image.go +++ b/ofdgo_extract_image.go @@ -40,7 +40,7 @@ type imageOutput struct { writer io.Writer } -// imageInput 在读取原始图片时检查调用方取消状态 +// imageInput 在读取资源数据时检查调用方取消状态 type imageInput struct { io.ReadCloser context context.Context