mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 08:46:55 +08:00
1 parent
2c96b4e490
commit
32ab7fe732
18 files changed
+81
-54
No files matched your search
+1
-1
@@ -1035,7 +1035,7 @@ func transformEditorObject(object GraphicObject, dx, dy, scale float64) (Graphic
|
||||
default:
|
||||
return GraphicObject{}, fmt.Errorf("unsupported object type %q", object.Type)
|
||||
}
|
||||
if clips := editorObjectClips(&object); scale != 1 && *clips != nil && (object.Type == "TextObject" || (*clips).TransFlag != nil && !*(*clips).TransFlag) {
|
||||
if clips := editorObjectClips(&object); scale != 1 && *clips != nil && (object.Type == "TextObject" || !(*clips).usesObjectMatrix()) {
|
||||
*clips = transformObjectClips(*clips, Matrix{a: scale, d: scale})
|
||||
}
|
||||
box, err := ParseBox(*boundary)
|
||||
|
||||
@@ -470,10 +470,8 @@ func (e *Editor) compositeMembers(n *editorCompositeNode, reader *Reader, render
|
||||
boundary := n.parent.Multiply(TranslationMatrix(box.X, box.Y))
|
||||
matrix := boundary.Multiply(NewMatrix(c.CTM))
|
||||
clips, clipMatrix := c.Clips, matrix
|
||||
if clips != nil && clips.TransFlag != nil && !*clips.TransFlag {
|
||||
copy := *clips
|
||||
copy.TransFlag = nil
|
||||
clips, clipMatrix = ©, boundary
|
||||
if !clips.usesObjectMatrix() {
|
||||
clipMatrix = boundary
|
||||
}
|
||||
clip := n.clip
|
||||
var err error
|
||||
@@ -658,7 +656,7 @@ func (e *Editor) measureCompositeMembers(renderer *Renderer, nodes []*editorComp
|
||||
}
|
||||
if transform && node.object.Type == "ImageObject" && bounds.Bounds.W > 0 && bounds.Bounds.H > 0 {
|
||||
clips := node.object.ImageObject.Clips
|
||||
_, capability.CropImage = node.matrix(clips == nil || clips.TransFlag == nil || *clips.TransFlag).Invert()
|
||||
_, capability.CropImage = node.matrix(clips.usesObjectMatrix()).Invert()
|
||||
capability.FitImage = capability.CropImage && axisAlignedMatrix(NewMatrix(node.object.ImageObject.CTM))
|
||||
capability.ResetCrop = node.states[editorObjectID(node.object)].crop != nil
|
||||
}
|
||||
@@ -903,7 +901,7 @@ func transformCompositeOffsets(data []byte, matrix Matrix) ([]byte, error) {
|
||||
}
|
||||
object := cloneEditorData(n.object)
|
||||
object = compositeBoundary(object, Matrix{a: matrix.a, b: matrix.b, c: matrix.c, d: matrix.d}, false)
|
||||
if clips := editorObjectClips(&object); *clips != nil && (*clips).TransFlag != nil && !*(*clips).TransFlag {
|
||||
if clips := editorObjectClips(&object); *clips != nil && !(*clips).usesObjectMatrix() {
|
||||
*clips = transformObjectClips(*clips, TranslationMatrix(matrix.e, matrix.f))
|
||||
}
|
||||
fragment, err = editorXMLObject(n.data, n.node, n.object, object)
|
||||
@@ -943,7 +941,7 @@ func compositeBoundary(object GraphicObject, parent Matrix, inward bool) Graphic
|
||||
case "ImageObject":
|
||||
object.ImageObject.Boundary = editorBoxString(box)
|
||||
}
|
||||
if clips := editorObjectClips(&object); *clips != nil && (*clips).TransFlag != nil && !*(*clips).TransFlag {
|
||||
if clips := editorObjectClips(&object); *clips != nil && !(*clips).usesObjectMatrix() {
|
||||
*clips = transformObjectClips(*clips, clipMatrix)
|
||||
}
|
||||
return object
|
||||
|
||||
@@ -299,7 +299,7 @@ func (e *Editor) pasteCompositeSelection(selection *CompositeSelection, paste fu
|
||||
return err
|
||||
}
|
||||
clips := *editorObjectClips(©.object)
|
||||
matrix, ok := copy.matrix(clips == nil || clips.TransFlag == nil || *clips.TransFlag).Invert()
|
||||
matrix, ok := copy.matrix(clips.usesObjectMatrix()).Invert()
|
||||
if !ok {
|
||||
return fmt.Errorf("composite clip transform is not invertible")
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func (e *Editor) CropCompositeImage(page int, path ObjectPath, index int, box Bo
|
||||
return fmt.Errorf("crop does not intersect the image")
|
||||
}
|
||||
clips := node.object.ImageObject.Clips
|
||||
inverse, _ := node.matrix(clips == nil || clips.TransFlag == nil || *clips.TransFlag).Invert()
|
||||
inverse, _ := node.matrix(clips.usesObjectMatrix()).Invert()
|
||||
shape, err := NewShape(ShapeRectangle, box)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -143,7 +143,7 @@ func (e *Editor) FitCompositeImage(page int, path ObjectPath, index int, mode st
|
||||
frame = TranslationMatrix(box.X, box.Y).Multiply(node.parent)
|
||||
}
|
||||
clips := object.ImageObject.Clips
|
||||
inverse, _ := node.matrix(clips == nil || clips.TransFlag == nil || *clips.TransFlag).Invert()
|
||||
inverse, _ := node.matrix(clips.usesObjectMatrix()).Invert()
|
||||
return appendCompositeClip(node, shape, inverse.Multiply(frame), true)
|
||||
})
|
||||
}
|
||||
@@ -192,7 +192,7 @@ func appendCompositeClip(node *editorCompositeNode, shape PathObject, matrix Mat
|
||||
state.crop = &crop
|
||||
}
|
||||
data, err := encodeOFDXML(func(x *ofdXML) {
|
||||
x.clips(&Clips{Clip: []Clip{clip}})
|
||||
x.clips(&Clips{Clip: []Clip{clip}}, "")
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -209,9 +209,13 @@ func appendCompositeClip(node *editorCompositeNode, shape PathObject, matrix Mat
|
||||
patch = editorXMLContent(node.data, original, content)
|
||||
} else {
|
||||
data = editorXMLEncodedFragment(data, root)
|
||||
if actions := node.node.child("Actions"); actions != nil {
|
||||
patch = editorXMLPatch{actions.end, actions.end, data}
|
||||
} else {
|
||||
content := append(data, node.data[node.node.open:node.node.close]...)
|
||||
patch = editorXMLContent(node.data, node.node, content)
|
||||
}
|
||||
}
|
||||
updated, err := newEditorCompositeNode(editorPatchXML(node.data, []editorXMLPatch{patch}))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -133,7 +133,7 @@ func (e *Editor) eraseCompositeObjects(page int, path ObjectPath, indexes []int,
|
||||
return fmt.Errorf("composite member cannot be erased")
|
||||
}
|
||||
clips := *editorObjectClips(&node.object)
|
||||
inverse, ok := node.matrix(clips == nil || clips.TransFlag == nil || *clips.TransFlag).Invert()
|
||||
inverse, ok := node.matrix(clips.usesObjectMatrix()).Invert()
|
||||
if !ok {
|
||||
return fmt.Errorf("composite clip transform is not invertible")
|
||||
}
|
||||
@@ -212,7 +212,7 @@ func (e *Editor) eraseObjects(page int, ids []string, box Box, points []Point, r
|
||||
*clips = &Clips{}
|
||||
}
|
||||
matrix := IdentityMatrix
|
||||
if (*clips).TransFlag == nil || *(*clips).TransFlag {
|
||||
if (*clips).usesObjectMatrix() {
|
||||
if object.Type == "ImageObject" && ctm == "" {
|
||||
ctm = Matrix{a: frame.W, d: frame.H}.String()
|
||||
}
|
||||
|
||||
@@ -261,6 +261,11 @@ func (x *ofdXML) referenceElement(name string, value any) {
|
||||
}
|
||||
var attrs ofdAttrs
|
||||
switch value := value.(type) {
|
||||
case *Dest:
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
attrs.add("PageID", value.PageID)
|
||||
case ShdColor:
|
||||
attrs.add("ColorSpace", value.ColorSpace)
|
||||
case *ShdColor:
|
||||
|
||||
@@ -456,7 +456,7 @@ func transformEditorMatrix(object GraphicObject, matrix Matrix) GraphicObject {
|
||||
local := TranslationMatrix(-after.X, -after.Y).Multiply(matrix).Multiply(TranslationMatrix(before.X, before.Y))
|
||||
*ctm = local.Multiply(NewMatrix(*ctm)).String()
|
||||
*boundary = editorBoxString(after)
|
||||
if clips := editorObjectClips(&object); *clips != nil && (*clips).TransFlag != nil && !*(*clips).TransFlag {
|
||||
if clips := editorObjectClips(&object); *clips != nil && !(*clips).usesObjectMatrix() {
|
||||
*clips = transformObjectClips(*clips, local)
|
||||
}
|
||||
if !axisAlignedMatrix(matrix) && before.W > 0 && before.H > 0 {
|
||||
@@ -469,13 +469,12 @@ func transformEditorMatrix(object GraphicObject, matrix Matrix) GraphicObject {
|
||||
*clips = ©
|
||||
}
|
||||
clipMatrix := local
|
||||
if (*clips).TransFlag == nil || *(*clips).TransFlag {
|
||||
if (*clips).usesObjectMatrix() {
|
||||
if inverse, ok := NewMatrix(*ctm).Invert(); ok {
|
||||
clipMatrix = inverse.Multiply(local)
|
||||
} else {
|
||||
*clips = transformObjectClips(*clips, NewMatrix(*ctm))
|
||||
flag := false
|
||||
(*clips).TransFlag = &flag
|
||||
(*clips).TransFlag = nil
|
||||
}
|
||||
}
|
||||
fill, stroke := true, false
|
||||
@@ -526,17 +525,13 @@ func cropImageObject(image *ImageObject, box Box) error {
|
||||
image.Boundary = editorBoxString(box)
|
||||
image.Clips = nil
|
||||
if box != full {
|
||||
inverse, ok := NewMatrix(image.CTM).Invert()
|
||||
if !ok {
|
||||
return fmt.Errorf("image transform is not invertible")
|
||||
}
|
||||
path, err := NewShape(ShapeRectangle, Box{W: box.W, H: box.H})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fill, stroke := true, false
|
||||
path.Fill, path.Stroke = &fill, &stroke
|
||||
image.Clips = &Clips{Clip: []Clip{{Area: []ClipArea{{CTM: inverse.String(), Path: []PathObject{path}}}}}}
|
||||
image.Clips = &Clips{Clip: []Clip{{Area: []ClipArea{{Path: []PathObject{path}}}}}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -373,6 +373,19 @@ func editorXMLObject(data []byte, node *editorXML, before, after GraphicObject)
|
||||
// 入参: data 原文, node 原节点, oldXML 修改前编码, newXML 修改后编码
|
||||
// 返回: []byte 更新节点, error 错误信息
|
||||
func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte, error) {
|
||||
legacyClip := node.name.Local == "Clips" && node.hasAttr("TransFlag")
|
||||
if legacyClip {
|
||||
var clips Clips
|
||||
if err := xml.Unmarshal(data[node.start:node.end], &clips); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clips.TransFlag = nil
|
||||
var err error
|
||||
oldXML, err = encodeOFDXML(func(x *ofdXML) { x.clips(&clips, "") })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
oldNode, err := parseEditorXML(oldXML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -382,6 +395,9 @@ func editorXMLMerge(data []byte, node *editorXML, oldXML, newXML []byte) ([]byte
|
||||
return nil, err
|
||||
}
|
||||
changes := make(map[string]string)
|
||||
if legacyClip {
|
||||
changes["TransFlag"] = ""
|
||||
}
|
||||
for _, attr := range append(oldNode.attrs, newNode.attrs...) {
|
||||
if attr.Name.Space == "" && (oldNode.attr(attr.Name.Local) != newNode.attr(attr.Name.Local) || oldNode.hasAttr(attr.Name.Local) != newNode.hasAttr(attr.Name.Local)) {
|
||||
changes[attr.Name.Local] = newNode.attr(attr.Name.Local)
|
||||
|
||||
@@ -47,7 +47,7 @@ type StrokeOptions struct {
|
||||
}
|
||||
|
||||
// GeometryBackend 操作库自有路径,坐标以页面左上角为原点,不修改任何输入
|
||||
// Clip返回nil表示不裁剪,空路径指针表示完全裁去,矩阵映射到页面坐标
|
||||
// Clip返回nil表示不裁剪,空路径指针表示完全裁去,矩阵将裁剪坐标映射到页面坐标
|
||||
type GeometryBackend interface {
|
||||
Backend
|
||||
Path(object PathObject) (GeometryPath, error)
|
||||
|
||||
@@ -47,6 +47,12 @@ func (o GraphicObject) Actions() []Action {
|
||||
return nil
|
||||
}
|
||||
|
||||
// usesObjectMatrix 判断旧式扩展是否要求裁剪跟随对象变换,标准裁剪以外接矩形为基准
|
||||
// 返回: bool 是否叠加对象CTM
|
||||
func (clips *Clips) usesObjectMatrix() bool {
|
||||
return clips != nil && clips.TransFlag != nil && *clips.TransFlag
|
||||
}
|
||||
|
||||
// UnmarshalXML 解析路径并区分省略样式、显式实线与零线宽
|
||||
// 入参: d XML解码器, start 起始节点
|
||||
// 返回: error 错误信息
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@ type GraphicObject struct {
|
||||
state editorCompositeState
|
||||
}
|
||||
|
||||
// Clips 裁剪区域集合
|
||||
// Clips 相对于对象外接矩形的裁剪集合
|
||||
// TransFlag仅兼容旧式扩展,写入时折算为Area的CTM
|
||||
type Clips struct {
|
||||
TransFlag *bool `xml:"TransFlag,attr"`
|
||||
Clip []Clip `xml:"Clip"`
|
||||
|
||||
+1
-1
@@ -502,7 +502,7 @@ func (p *pdfImporter) clips(paths []pdfgo.Path, origin Box) (*Clips, error) {
|
||||
continue
|
||||
}
|
||||
if clips == nil {
|
||||
clips = &Clips{TransFlag: new(false), Clip: make([]Clip, 0, len(paths))}
|
||||
clips = &Clips{Clip: make([]Clip, 0, len(paths))}
|
||||
areas = make([]ClipArea, len(paths))
|
||||
if origin.X != 0 || origin.Y != 0 {
|
||||
translation = pdfNumbers(1, 0, 0, 1, -origin.X, -origin.Y)
|
||||
|
||||
@@ -74,7 +74,7 @@ func pdfPageClip(object GraphicObject) *Clips {
|
||||
return nil
|
||||
}
|
||||
clips := *editorObjectClips(&object)
|
||||
if clips == nil || clips.TransFlag == nil || *clips.TransFlag || len(clips.Clip) == 0 {
|
||||
if clips == nil || clips.usesObjectMatrix() || len(clips.Clip) == 0 {
|
||||
return nil
|
||||
}
|
||||
boundary, _ := editorGeometry(object)
|
||||
@@ -87,7 +87,7 @@ func pdfPageClip(object GraphicObject) *Clips {
|
||||
translation = pdfNumbers(1, 0, 0, 1, -box.X, -box.Y)
|
||||
}
|
||||
bytes := 0
|
||||
result := &Clips{TransFlag: clips.TransFlag, Clip: make([]Clip, len(clips.Clip))}
|
||||
result := &Clips{Clip: make([]Clip, len(clips.Clip))}
|
||||
for index, clip := range clips.Clip {
|
||||
if len(clip.Area) != 1 || clip.Area[0].CTM != translation {
|
||||
return nil
|
||||
@@ -113,7 +113,7 @@ func pdfMatchesPageClip(object GraphicObject, pageClip *Clips) bool {
|
||||
return false
|
||||
}
|
||||
clips := *editorObjectClips(&object)
|
||||
if clips == nil || clips.TransFlag == nil || *clips.TransFlag || len(clips.Clip) != len(pageClip.Clip) {
|
||||
if clips == nil || clips.usesObjectMatrix() || len(clips.Clip) != len(pageClip.Clip) {
|
||||
return false
|
||||
}
|
||||
boundary, _ := editorGeometry(object)
|
||||
|
||||
@@ -373,10 +373,7 @@ func (r *Renderer) objectGeometryClip(clips *Clips, boundary string, local Matri
|
||||
return clip, nil
|
||||
}
|
||||
matrix, _ := renderObjectMatrix(boundary, local, state)
|
||||
if clips.TransFlag != nil && !*clips.TransFlag {
|
||||
copy := *clips
|
||||
copy.TransFlag = nil
|
||||
clips = ©
|
||||
if !clips.usesObjectMatrix() {
|
||||
box, _ := ParseBox(boundary)
|
||||
matrix = TranslationMatrix(box.X, box.Y)
|
||||
if state.BoundaryInCTM && state.Parent != nil {
|
||||
|
||||
@@ -225,10 +225,8 @@ func (r *Renderer) walkComposite(object CompositeGraphicUnit, state RenderState,
|
||||
return err
|
||||
}
|
||||
clips, clipMatrix := object.Clips, matrix
|
||||
if clips != nil && clips.TransFlag != nil && !*clips.TransFlag {
|
||||
copy := *clips
|
||||
copy.TransFlag = nil
|
||||
clips, clipMatrix = ©, boundary
|
||||
if !clips.usesObjectMatrix() {
|
||||
clipMatrix = boundary
|
||||
}
|
||||
if clips != nil {
|
||||
geometry, err := r.Geometry()
|
||||
|
||||
@@ -86,9 +86,9 @@ func (r *Renderer) buildObjectClipPath(clips *Clips, pageH float64, boundary str
|
||||
}
|
||||
|
||||
// buildClipPath 构建裁剪路径
|
||||
// 入参: clips 裁剪对象, pageH 页面高度, bx 边界X坐标, by 边界Y坐标, objectCTM 对象CTM
|
||||
// 入参: clips 裁剪对象, pageH 页面高度, bx 边界X坐标, by 边界Y坐标, matrix 裁剪坐标到页面的变换
|
||||
// 返回: *canvas.Path 路径对象
|
||||
func (r *Renderer) buildClipPath(clips *Clips, pageH float64, bx, by float64, objectCTM Matrix) *canvas.Path {
|
||||
func (r *Renderer) buildClipPath(clips *Clips, pageH float64, bx, by float64, matrix Matrix) *canvas.Path {
|
||||
if clips == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -96,10 +96,7 @@ func (r *Renderer) buildClipPath(clips *Clips, pageH float64, bx, by float64, ob
|
||||
for _, clip := range clips.Clip {
|
||||
renderer := &clipRenderer{}
|
||||
for _, area := range clip.Area {
|
||||
areaCTM := NewMatrix(area.CTM)
|
||||
if clips.TransFlag == nil || *clips.TransFlag {
|
||||
areaCTM = objectCTM.Multiply(areaCTM)
|
||||
}
|
||||
areaCTM := matrix.Multiply(NewMatrix(area.CTM))
|
||||
for _, pathObj := range area.Path {
|
||||
renderer.add(r.buildClipAreaPath(pathObj, area.DrawParam, areaCTM, pageH, bx, by))
|
||||
if r.renderError != nil {
|
||||
|
||||
@@ -181,7 +181,7 @@ func (CanvasBackend) Stroke(path GeometryPath, options StrokeOptions) (result Ge
|
||||
}
|
||||
|
||||
// Clip 解析对象裁剪并与父裁剪相交,保留未裁剪与完全裁去的区别
|
||||
// 入参: r 渲染器, clips OFD裁剪, matrix 页面变换, parent 父裁剪
|
||||
// 入参: r 渲染器, clips OFD裁剪, matrix 裁剪坐标到页面的变换, parent 父裁剪
|
||||
// 返回: *GeometryPath 页面裁剪, error 无效父路径错误
|
||||
func (CanvasBackend) Clip(r *Renderer, clips *Clips, matrix Matrix, parent *GeometryPath) (*GeometryPath, error) {
|
||||
p, err := geometryToCanvasPath(parent)
|
||||
|
||||
+20
-10
@@ -870,17 +870,22 @@ func (x *ofdXML) object(object GraphicObject, root bool) {
|
||||
x.start(object.Type, attrs)
|
||||
x.actions(actions)
|
||||
if object.Type == "ImageObject" {
|
||||
x.clips(object.ImageObject.Clips)
|
||||
ctm := object.ImageObject.CTM
|
||||
if ctm == "" && object.ImageObject.Clips.usesObjectMatrix() {
|
||||
box, _ := ParseBox(object.ImageObject.Boundary)
|
||||
ctm = (Matrix{a: box.W, d: box.H}).String()
|
||||
}
|
||||
x.clips(object.ImageObject.Clips, ctm)
|
||||
x.border(object.ImageObject.Border)
|
||||
} else if object.Type == "CompositeObject" || object.Type == "CompositeGraphicUnit" {
|
||||
x.clips(object.CompositeGraphicUnit.Clips)
|
||||
x.clips(object.CompositeGraphicUnit.Clips, object.CompositeGraphicUnit.CTM)
|
||||
} else if object.Type == "PathObject" || object.Type == "Path" {
|
||||
x.clips(object.PathObject.Clips)
|
||||
x.clips(object.PathObject.Clips, object.PathObject.CTM)
|
||||
x.color("StrokeColor", stroke)
|
||||
x.color("FillColor", fill)
|
||||
x.text("AbbreviatedData", object.PathObject.AbbreviatedData)
|
||||
} else if object.Type == "TextObject" || object.Type == "Text" {
|
||||
x.clips(object.TextObject.Clips)
|
||||
x.clips(object.TextObject.Clips, object.TextObject.CTM)
|
||||
x.color("FillColor", fill)
|
||||
x.color("StrokeColor", stroke)
|
||||
offset, index := 0, 0
|
||||
@@ -917,15 +922,17 @@ func (x *ofdXML) object(object GraphicObject, root bool) {
|
||||
x.end(object.Type)
|
||||
}
|
||||
|
||||
// clips 写出对象裁剪,保留区域内的路径与文字
|
||||
// 入参: clips 裁剪集合,nil不输出节点
|
||||
func (x *ofdXML) clips(clips *Clips) {
|
||||
// clips 写出标准裁剪,将旧式随动裁剪折算为区域变换
|
||||
// 入参: clips 裁剪集合,nil不输出节点, ctm 对象变换矩阵
|
||||
func (x *ofdXML) clips(clips *Clips, ctm string) {
|
||||
if clips == nil || !x.checkContext() {
|
||||
return
|
||||
}
|
||||
var attrs ofdAttrs
|
||||
attrs.flag("TransFlag", clips.TransFlag)
|
||||
x.start("Clips", attrs)
|
||||
matrix := IdentityMatrix
|
||||
if clips.usesObjectMatrix() {
|
||||
matrix = NewMatrix(ctm)
|
||||
}
|
||||
x.start("Clips", nil)
|
||||
for _, clip := range clips.Clip {
|
||||
if x.err != nil {
|
||||
return
|
||||
@@ -936,6 +943,9 @@ func (x *ofdXML) clips(clips *Clips) {
|
||||
return
|
||||
}
|
||||
var attrs ofdAttrs
|
||||
if clips.usesObjectMatrix() {
|
||||
area.CTM = matrix.Multiply(NewMatrix(area.CTM)).String()
|
||||
}
|
||||
attrs.add("CTM", area.CTM)
|
||||
attrs.add("DrawParam", area.DrawParam)
|
||||
for _, path := range area.Path {
|
||||
|
||||
Reference in new issue
Block a user