feat(编辑能力): 完善内部对象编辑,修复文字重框与复制异常
build.yaml / build (push) Successful in 39s

This commit is contained in:
xiaoqidun committed 2026-09-17 20:27:58 +08:00
1 parent 0f86c8d727
commit f8eeaad9f2
16 files changed
+546 -109

No files matched your search

+69 -7
View File
@@ -41,13 +41,25 @@ func (e *Editor) EraseObjects(page int, ids []string, box Box) error {
// 入参: page 页面索引, ids 对象标识, points 页面毫米坐标,至少三个点
// 返回: error 错误信息
func (e *Editor) EraseObjectsPath(page int, ids []string, points []Point) error {
region, err := editorEraseRegion(points)
if err != nil || region.Empty() {
return err
}
bounds := region.FastBounds()
return e.eraseObjects(page, ids, Box{X: bounds.X0, Y: bounds.Y0, W: bounds.W(), H: bounds.H()}, points, region)
}
// editorEraseRegion 校验闭合折线并按奇偶规则构建擦除区域
// 入参: points 页面毫米坐标
// 返回: *canvas.Path 擦除区域, error 错误信息
func editorEraseRegion(points []Point) (*canvas.Path, error) {
if len(points) < 3 {
return fmt.Errorf("erase path requires at least three points")
return nil, fmt.Errorf("erase path requires at least three points")
}
region := &canvas.Path{}
for i, point := range points {
if !finite(point.X) || !finite(point.Y) {
return fmt.Errorf("invalid erase path point %d", i)
return nil, fmt.Errorf("invalid erase path point %d", i)
}
if i == 0 {
region.MoveTo(point.X, point.Y)
@@ -56,12 +68,62 @@ func (e *Editor) EraseObjectsPath(page int, ids []string, points []Point) error
}
}
region.Close()
region = region.Settle(canvas.EvenOdd)
if region.Empty() {
return nil
return region.Settle(canvas.EvenOdd), nil
}
// EraseCompositeObjects 擦除内部成员与矩形相交的部分,保留原始属性及共享资源
// 全部覆盖时删除成员,部分覆盖追加标准裁剪,不用于敏感信息脱敏
// 入参: page 页面索引, path 父路径, indexes 成员序号, box 页面毫米范围
// 返回: error 错误信息
func (e *Editor) EraseCompositeObjects(page int, path ObjectPath, indexes []int, box Box) error {
if _, err := creationBox(editorBoxString(box)); err != nil {
return err
}
bounds := region.FastBounds()
return e.eraseObjects(page, ids, Box{X: bounds.X0, Y: bounds.Y0, W: bounds.W(), H: bounds.H()}, points, region)
return e.eraseCompositeObjects(page, path, indexes, canvas.Rectangle(box.W, box.H).Translate(box.X, box.Y))
}
// EraseCompositeObjectsPath 按闭合折线擦除内部成员,末点自动连接起点
// 与矩形擦除共用原子提交和撤销逻辑,不用于敏感信息脱敏
// 入参: page 页面索引, path 父路径, indexes 成员序号, points 页面毫米坐标
// 返回: error 错误信息
func (e *Editor) EraseCompositeObjectsPath(page int, path ObjectPath, indexes []int, points []Point) error {
region, err := editorEraseRegion(points)
if err != nil || region.Empty() {
return err
}
return e.eraseCompositeObjects(page, path, indexes, region)
}
// eraseCompositeObjects 按页面区域裁剪内部成员,全部成功后提交一次历史
// 入参: page 页面索引, path 父路径, indexes 成员序号, region 擦除区域
// 返回: error 错误信息
func (e *Editor) eraseCompositeObjects(page int, path ObjectPath, indexes []int, region *canvas.Path) error {
return e.editCompositeObjects(page, path, indexes, func(_ *Renderer, nodes []*editorCompositeNode, members []CompositeMember) error {
for i, node := range nodes {
box := members[i].Bounds
bounds := canvas.Rectangle(box.W, box.H).Translate(box.X, box.Y)
if box.W <= 0 || box.H <= 0 || bounds.And(region).Empty() {
continue
}
if bounds.Not(region).Empty() {
node.owner.patches = append(node.owner.patches, editorXMLPatch{node.span.start, node.span.end, nil})
continue
}
if !members[i].Capabilities.Arrange {
return fmt.Errorf("composite member cannot be erased")
}
clips := *editorObjectClips(&node.object)
inverse, ok := node.matrix(clips == nil || clips.TransFlag == nil || *clips.TransFlag).Invert()
if !ok {
return fmt.Errorf("composite clip transform is not invertible")
}
outer := canvas.Rectangle(box.W+2, box.H+2).Translate(box.X-1, box.Y-1)
if err := appendCompositeClip(node, editorClipPath(outer.Not(region)), inverse, false); err != nil {
return err
}
}
return nil
})
}
// eraseObjects 按矩形或闭合路径裁剪对象,只在全部对象处理成功后提交修改