From 54995fb60e4f720936ce32f3dbc876dc13f6eda3 Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Sat, 3 Oct 2026 18:36:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B8=B2=E6=9F=93=E5=BC=95=E6=93=8E):=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A3=81=E5=89=AA=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 +- go.sum | 4 ++-- ofdgo_editor_documents.go | 9 +++++++- ofdgo_editor_import.go | 1 + ofdgo_editor_open.go | 1 + ofdgo_pdf.go | 2 +- ofdgo_pdf_object.go | 22 ++++++++++++++----- ofdgo_renderer_clip_canvas.go | 37 ++++++++++++++++++++++++-------- ofdgo_renderer_path_canvas.go | 2 +- ofdgo_renderer_shading_canvas.go | 4 ++-- 10 files changed, 62 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 97683f6..bdc6c43 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,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-20261003042717-ffb073d5634d + github.com/xiaoqidun/pdfgo v0.0.0-20261003103143-2420f94d9b97 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 3546da7..d523567 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-20261003042717-ffb073d5634d h1:WiG6LgYU1hZBAELi72qP6UnCyA8cPe7Ap6nto0ofGnA= -github.com/xiaoqidun/pdfgo v0.0.0-20261003042717-ffb073d5634d/go.mod h1:4Pf0JO0BEf1uzRY+uZjlMjN/OPn3PYa0YMi6cqPPTLQ= +github.com/xiaoqidun/pdfgo v0.0.0-20261003103143-2420f94d9b97 h1:1YHWQ3BBFJd4RlWAEGhHHQ6D+OI/nriU91YIUzxZo8o= +github.com/xiaoqidun/pdfgo v0.0.0-20261003103143-2420f94d9b97/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/ofdgo_editor_documents.go b/ofdgo_editor_documents.go index 2b86f43..635fb77 100644 --- a/ofdgo_editor_documents.go +++ b/ofdgo_editor_documents.go @@ -330,7 +330,14 @@ func (e *Editor) documentRoot() string { // 返回: bool 是否为同一文档 func (e *Editor) sameDocument(other *Editor) bool { if e.source == nil || other.source == nil { - return e.source == nil && other.source == nil + if e.source == nil && other.source == nil { + return true + } + fresh, packaged := e, other + if fresh.source != nil { + fresh, packaged = other, e + } + return len(packaged.source.reader.OFD.DocBody) == 1 && (packaged.source.fromNew || fresh.Info.DocID != "" && fresh.Info.DocID == packaged.source.info.DocID) } return e.documentRoot() == other.documentRoot() && e.source.info.DocID == other.source.info.DocID } diff --git a/ofdgo_editor_import.go b/ofdgo_editor_import.go index 0ba966d..f591de1 100644 --- a/ofdgo_editor_import.go +++ b/ofdgo_editor_import.go @@ -684,6 +684,7 @@ func (e *Editor) importBase() (*editorSource, error) { return nil, err } editor.source.idsReady = true + editor.source.fromNew = true return editor.source, nil } diff --git a/ofdgo_editor_open.go b/ofdgo_editor_open.go index b1e062d..82382b3 100644 --- a/ofdgo_editor_open.go +++ b/ofdgo_editor_open.go @@ -36,6 +36,7 @@ type editorSource struct { info DocInfo fallbackDocID string directory string + fromNew bool pages map[string]*editorSourcePage origins map[string]*editorObjectOrigin idsReady bool diff --git a/ofdgo_pdf.go b/ofdgo_pdf.go index 013224a..7c64b57 100644 --- a/ofdgo_pdf.go +++ b/ofdgo_pdf.go @@ -78,7 +78,7 @@ type pdfImporter struct { pages map[pdfgo.Reference]*pdfgo.Page pageIDs map[pdfgo.Reference]string pageIndexes map[pdfgo.Reference]int - imageIDs map[pdfImageKey]string + imageIDs map[pdfImageKey][]pdfImageResource attachmentIDs map[pdfAttachmentKey]string maskClips map[*pdfgo.SoftMask]pdfgo.Path pageBox pdfgo.Rectangle diff --git a/ofdgo_pdf_object.go b/ofdgo_pdf_object.go index ddd9501..f33adbf 100644 --- a/ofdgo_pdf_object.go +++ b/ofdgo_pdf_object.go @@ -20,6 +20,7 @@ import ( "image" "image/color" "math" + "reflect" "strconv" "unicode/utf8" @@ -32,6 +33,12 @@ type pdfImageKey struct { intent pdfgo.Name } +// pdfImageResource 保存图像的有效颜色空间及对应资源,避免跨资源重映射复用 +type pdfImageResource struct { + space pdfgo.Object + id string +} + // pdfStencilImage 按当前填充色读取模板图像,不分配整幅彩色样本 type pdfStencilImage struct { source image.Image @@ -103,9 +110,14 @@ func (p *pdfImporter) image(mark pdfgo.ImageMark) error { } source := mark.Image key := pdfImageKey{stream: source.Stream, intent: source.Intent} - id := p.imageIDs[key] - if source.ImageMask { - id = "" + id := "" + if !source.ImageMask { + for _, resource := range p.imageIDs[key] { + if reflect.DeepEqual(resource.space, source.ColorSpace) { + id = resource.id + break + } + } } if id == "" { jbig2Original, err := source.JBIG2File() @@ -155,9 +167,9 @@ func (p *pdfImporter) image(mark pdfgo.ImageMark) error { } if !source.ImageMask { if p.imageIDs == nil { - p.imageIDs = map[pdfImageKey]string{} + p.imageIDs = map[pdfImageKey][]pdfImageResource{} } - p.imageIDs[key] = id + p.imageIDs[key] = append(p.imageIDs[key], pdfImageResource{space: source.ColorSpace, id: id}) } } return p.appendImage(mark, id) diff --git a/ofdgo_renderer_clip_canvas.go b/ofdgo_renderer_clip_canvas.go index 6eb3215..57bb920 100644 --- a/ofdgo_renderer_clip_canvas.go +++ b/ofdgo_renderer_clip_canvas.go @@ -160,11 +160,23 @@ func intersectClipPath(parent, current *canvas.Path) *canvas.Path { if parent.Empty() || current.Empty() { return &canvas.Path{} } - if parentOK && parentRect.Contains(current.FastBounds()) { - return current + if parentOK { + bounds := current.Bounds() + if parentRect.Contains(bounds) { + return current + } + if !parentRect.Overlaps(bounds) { + return &canvas.Path{} + } } - if currentOK && currentRect.Contains(parent.FastBounds()) { - return parent + if currentOK { + bounds := parent.Bounds() + if currentRect.Contains(bounds) { + return parent + } + if !currentRect.Overlaps(bounds) { + return &canvas.Path{} + } } if result, ok := intersectConvexCanvasPaths(parent, current); ok { return result @@ -199,7 +211,7 @@ func applyClipPath(path, clip *canvas.Path) *canvas.Path { return &canvas.Path{} } if rect, ok := rectangularPath(clip); ok { - bounds := path.FastBounds() + bounds := path.Bounds() if rect.Contains(bounds) { return path } @@ -224,18 +236,25 @@ func rectangularPath(path *canvas.Path) (canvas.Rect, bool) { return canvas.Rect{}, false } data := path.Data() + if len(data) != 20 { + return canvas.Rect{}, false + } for i := 0; i < len(data); i += 4 { - if i+4 > len(data) || (data[i] != canvas.MoveToCmd && data[i] != canvas.LineToCmd && data[i] != canvas.CloseCmd) { + if data[i] != canvas.MoveToCmd && data[i] != canvas.LineToCmd && data[i] != canvas.CloseCmd { return canvas.Rect{}, false } } - points := path.Coords() - if len(points) != 5 || !points[0].Equals(points[4]) { + if !(canvas.Point{X: data[1], Y: data[2]}).Equals(canvas.Point{X: data[17], Y: data[18]}) { return canvas.Rect{}, false } rect := path.FastBounds() corners := 0 - for _, point := range points[:4] { + for i := 0; i < 16; i += 4 { + point := canvas.Point{X: data[i+1], Y: data[i+2]} + next := (i + 4) % 16 + if !canvas.Equal(point.X, data[next+1]) && !canvas.Equal(point.Y, data[next+2]) { + return canvas.Rect{}, false + } corner := 0 switch { case canvas.Equal(point.X, rect.X0) && canvas.Equal(point.Y, rect.Y0): diff --git a/ofdgo_renderer_path_canvas.go b/ofdgo_renderer_path_canvas.go index 2b3142b..855db30 100644 --- a/ofdgo_renderer_path_canvas.go +++ b/ofdgo_renderer_path_canvas.go @@ -368,7 +368,7 @@ func (r *Renderer) renderPattern(ctx *canvas.Context, pattern *PatternPaint, pag if !ok { return } - bounds := clip.FastBounds() + bounds := clip.Bounds() points := [][2]float64{ {bounds.X0, pageH - bounds.Y0}, {bounds.X1, pageH - bounds.Y0}, diff --git a/ofdgo_renderer_shading_canvas.go b/ofdgo_renderer_shading_canvas.go index d2a11fb..d667d5c 100644 --- a/ofdgo_renderer_shading_canvas.go +++ b/ofdgo_renderer_shading_canvas.go @@ -60,7 +60,7 @@ func (r *Renderer) drawSampledShading(ctx *canvas.Context, path *canvas.Path, vi if path.Empty() { return } - bounds := path.FastBounds().And(shdCanvasBounds(ctx)) + bounds := path.Bounds().And(shdCanvasBounds(ctx)) if bounds.W() <= 0 || bounds.H() <= 0 { return } @@ -243,7 +243,7 @@ func drawRepeatAxialPath(ctx *canvas.Context, path *canvas.Path, gradient *repea p = p.Transform(view.Inv()) d := gradient.End.Sub(gradient.Start).Mul(gradient.period) axis := canvas.Matrix{{d.X, -d.Y, gradient.Start.X}, {d.Y, d.X, gradient.Start.Y}} - bounds := p.FastBounds().And(shdCanvasBounds(ctx).Transform(view.Inv())) + bounds := p.Bounds().And(shdCanvasBounds(ctx).Transform(view.Inv())) area := bounds.Transform(axis.Inv()) origin := ctx.CoordView().Dot(canvas.Point{}) m := ctx.CoordSystemView().Mul(ctx.View()).Translate(origin.X, origin.Y).Mul(view)