mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
5e0da3da28
commit
ee6c9f6a16
82 files changed
+4316
-4310
No files matched your search
+100
-100
@@ -119,6 +119,106 @@ func (e *Editor) ResizeObjects(page int, ids []string, box Box) error {
|
||||
})
|
||||
}
|
||||
|
||||
// ImageBounds 返回完整图片经Boundary位移及CTM变换后的轴对齐边界,不应用裁剪或父级变换
|
||||
// 返回: Box 所在坐标系中的完整图片边界, error 错误信息
|
||||
func (obj ImageObject) ImageBounds() (Box, error) {
|
||||
box, err := creationBox(obj.Boundary)
|
||||
if err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
if obj.CTM == "" {
|
||||
return box, nil
|
||||
}
|
||||
if _, err := creationNumbers(obj.CTM, 6); err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
return TranslationMatrix(box.X, box.Y).Multiply(NewMatrix(obj.CTM)).TransformBox(Box{W: 1, H: 1}), nil
|
||||
}
|
||||
|
||||
// CropImage 按页面毫米坐标重设图片裁剪,保留原始资源及像素,可再次扩大裁剪区域
|
||||
// 使用ImageObject.ImageBounds返回的范围可还原完整图片;提交一次撤销记录
|
||||
// 入参: page 页面索引, id 图片对象标识, box 页面毫米坐标中的保留范围
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) CropImage(page int, id string, box Box) error {
|
||||
object, err := e.Object(page, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if object.Type != "ImageObject" {
|
||||
return fmt.Errorf("object %q is not an image", id)
|
||||
}
|
||||
image := &object.ImageObject
|
||||
if err := cropImageObject(image, box); err != nil {
|
||||
return err
|
||||
}
|
||||
object.state.crop = nil
|
||||
return e.UpdateObject(page, id, object)
|
||||
}
|
||||
|
||||
// FitImage 按原始像素比例居中适应或填充当前Boundary,保留直角旋转及镜像方向
|
||||
// 保留原始图片,不重采样;重设裁剪,一次撤销恢复原布局
|
||||
// 入参: page 页面索引, id 图片对象标识, mode 为contain(完整显示)或cover(填满裁剪)
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) FitImage(page int, id, mode string) error {
|
||||
object, err := e.Object(page, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if object.Type != "ImageObject" {
|
||||
return fmt.Errorf("object %q is not an image", id)
|
||||
}
|
||||
if err := e.fitImage(&object.ImageObject, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
object.state.crop = nil
|
||||
return e.UpdateObject(page, id, object)
|
||||
}
|
||||
|
||||
// TextFrame 将对象Boundary逆变换到本地坐标并返回轴对齐边界
|
||||
// 直角旋转与翻转不改变本地段落宽度
|
||||
// 返回: Box 对象本地坐标中的边界, error 错误信息
|
||||
func (obj TextObject) TextFrame() (Box, error) {
|
||||
box, err := creationBox(obj.Boundary)
|
||||
if err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
if obj.CTM != "" {
|
||||
if _, err := creationNumbers(obj.CTM, 6); err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
}
|
||||
m, ok := NewMatrix(obj.CTM).Invert()
|
||||
if !ok {
|
||||
return Box{}, fmt.Errorf("text transform is not invertible")
|
||||
}
|
||||
return m.TransformBox(Box{W: box.W, H: box.H}), nil
|
||||
}
|
||||
|
||||
// ResizeTextFrame 调整文字本地坐标的左侧偏移及宽度,保留页面方向;重新排版后通过UpdateObject提交
|
||||
// 支持轴向缩放、直角旋转与镜像,不支持斜切或其他角度旋转
|
||||
// 入参: offset 本地左侧位移, width 新的本地排版宽度,单位为毫米
|
||||
// 返回: TextObject 调整后的文字对象, error 错误信息
|
||||
func (obj TextObject) ResizeTextFrame(offset, width float64) (TextObject, error) {
|
||||
frame, err := obj.TextFrame()
|
||||
if err != nil {
|
||||
return TextObject{}, err
|
||||
}
|
||||
if !axisAlignedMatrix(NewMatrix(obj.CTM)) {
|
||||
return TextObject{}, fmt.Errorf("text frame resizing requires an axis-aligned transform")
|
||||
}
|
||||
if !finite(offset) || !finite(width) || width <= 0 {
|
||||
return TextObject{}, fmt.Errorf("text frame requires a finite offset and a positive width")
|
||||
}
|
||||
boundary, _ := ParseBox(obj.Boundary)
|
||||
m := TranslationMatrix(boundary.X, boundary.Y).Multiply(NewMatrix(obj.CTM))
|
||||
frame.X += offset
|
||||
frame.W = width
|
||||
box := m.TransformBox(frame)
|
||||
obj.CTM = TranslationMatrix(-box.X, -box.Y).Multiply(m).Multiply(TranslationMatrix(offset, 0)).String()
|
||||
obj.Boundary = editorBoxString(box)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// transformObject 对原有复杂对象仅更新变换,不重建其文字、画刷或其他局部属性
|
||||
// 入参: object 对象, dx、dy 页面位移, scale 缩放比例
|
||||
// 返回: GraphicObject 新对象, error 错误信息
|
||||
@@ -345,42 +445,6 @@ func transformObjectClips(clips *Clips, matrix Matrix) *Clips {
|
||||
return &result
|
||||
}
|
||||
|
||||
// ImageBounds 返回完整图片经Boundary位移及CTM变换后的轴对齐边界,不应用裁剪或父级变换
|
||||
// 返回: Box 所在坐标系中的完整图片边界, error 错误信息
|
||||
func (obj ImageObject) ImageBounds() (Box, error) {
|
||||
box, err := creationBox(obj.Boundary)
|
||||
if err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
if obj.CTM == "" {
|
||||
return box, nil
|
||||
}
|
||||
if _, err := creationNumbers(obj.CTM, 6); err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
return TranslationMatrix(box.X, box.Y).Multiply(NewMatrix(obj.CTM)).TransformBox(Box{W: 1, H: 1}), nil
|
||||
}
|
||||
|
||||
// CropImage 按页面毫米坐标重设图片裁剪,保留原始资源及像素,可再次扩大裁剪区域
|
||||
// 使用ImageObject.ImageBounds返回的范围可还原完整图片;提交一次撤销记录
|
||||
// 入参: page 页面索引, id 图片对象标识, box 页面毫米坐标中的保留范围
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) CropImage(page int, id string, box Box) error {
|
||||
object, err := e.Object(page, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if object.Type != "ImageObject" {
|
||||
return fmt.Errorf("object %q is not an image", id)
|
||||
}
|
||||
image := &object.ImageObject
|
||||
if err := cropImageObject(image, box); err != nil {
|
||||
return err
|
||||
}
|
||||
object.state.crop = nil
|
||||
return e.UpdateObject(page, id, object)
|
||||
}
|
||||
|
||||
// cropImageObject 更新图片副本的裁剪和局部坐标,不修改资源
|
||||
// 入参: image 图片对象副本, box 页面毫米坐标中的保留范围
|
||||
// 返回: error 错误信息
|
||||
@@ -420,25 +484,6 @@ func cropImageObject(image *ImageObject, box Box) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FitImage 按原始像素比例居中适应或填充当前Boundary,保留直角旋转及镜像方向
|
||||
// 保留原始图片,不重采样;重设裁剪,一次撤销恢复原布局
|
||||
// 入参: page 页面索引, id 图片对象标识, mode 为contain(完整显示)或cover(填满裁剪)
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) FitImage(page int, id, mode string) error {
|
||||
object, err := e.Object(page, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if object.Type != "ImageObject" {
|
||||
return fmt.Errorf("object %q is not an image", id)
|
||||
}
|
||||
if err := e.fitImage(&object.ImageObject, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
object.state.crop = nil
|
||||
return e.UpdateObject(page, id, object)
|
||||
}
|
||||
|
||||
// fitImage 将原始像素比例应用到当前图片框,适应时保留空白,填充时裁掉超出部分
|
||||
// 入参: obj 图片对象副本, mode 为contain或cover
|
||||
// 返回: error 错误信息
|
||||
@@ -484,51 +529,6 @@ func (e *Editor) fitImage(obj *ImageObject, mode string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TextFrame 将对象Boundary逆变换到本地坐标并返回轴对齐边界
|
||||
// 直角旋转与翻转不改变本地段落宽度
|
||||
// 返回: Box 对象本地坐标中的边界, error 错误信息
|
||||
func (obj TextObject) TextFrame() (Box, error) {
|
||||
box, err := creationBox(obj.Boundary)
|
||||
if err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
if obj.CTM != "" {
|
||||
if _, err := creationNumbers(obj.CTM, 6); err != nil {
|
||||
return Box{}, err
|
||||
}
|
||||
}
|
||||
m, ok := NewMatrix(obj.CTM).Invert()
|
||||
if !ok {
|
||||
return Box{}, fmt.Errorf("text transform is not invertible")
|
||||
}
|
||||
return m.TransformBox(Box{W: box.W, H: box.H}), nil
|
||||
}
|
||||
|
||||
// ResizeTextFrame 调整文字本地坐标的左侧偏移及宽度,保留页面方向;重新排版后通过UpdateObject提交
|
||||
// 支持轴向缩放、直角旋转与镜像,不支持斜切或其他角度旋转
|
||||
// 入参: offset 本地左侧位移, width 新的本地排版宽度,单位为毫米
|
||||
// 返回: TextObject 调整后的文字对象, error 错误信息
|
||||
func (obj TextObject) ResizeTextFrame(offset, width float64) (TextObject, error) {
|
||||
frame, err := obj.TextFrame()
|
||||
if err != nil {
|
||||
return TextObject{}, err
|
||||
}
|
||||
if !axisAlignedMatrix(NewMatrix(obj.CTM)) {
|
||||
return TextObject{}, fmt.Errorf("text frame resizing requires an axis-aligned transform")
|
||||
}
|
||||
if !finite(offset) || !finite(width) || width <= 0 {
|
||||
return TextObject{}, fmt.Errorf("text frame requires a finite offset and a positive width")
|
||||
}
|
||||
boundary, _ := ParseBox(obj.Boundary)
|
||||
m := TranslationMatrix(boundary.X, boundary.Y).Multiply(NewMatrix(obj.CTM))
|
||||
frame.X += offset
|
||||
frame.W = width
|
||||
box := m.TransformBox(frame)
|
||||
obj.CTM = TranslationMatrix(-box.X, -box.Y).Multiply(m).Multiply(TranslationMatrix(offset, 0)).String()
|
||||
obj.Boundary = editorBoxString(box)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// editorBoxString 使用统一精度序列化创作边界
|
||||
// 入参: box 矩形范围
|
||||
// 返回: string 标准Boundary属性值
|
||||
|
||||
Reference in new issue
Block a user