mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 08:46:55 +08:00
This commit is contained in:
1 parent
0508029387
commit
d7cd3d03b5
20 files changed
+1158
-137
No files matched your search
+196
-5
@@ -23,6 +23,70 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PatternStyle 图案尺寸、步进和变换增量,nil字段保持原值,单位为毫米
|
||||
type PatternStyle struct {
|
||||
Width *float64
|
||||
Height *float64
|
||||
XStep *float64
|
||||
YStep *float64
|
||||
CTM *string
|
||||
}
|
||||
|
||||
// StylePatterns 修改已有图案布局,保留单元内容、资源引用和未知扩展
|
||||
// 入参: page 页面索引, ids 对象标识, stroke 是否修改描边, style 图案布局增量
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) StylePatterns(page int, ids []string, stroke bool, style PatternStyle) error {
|
||||
for _, size := range []*float64{style.Width, style.Height} {
|
||||
if size != nil && (!finite(*size) || *size <= 0) {
|
||||
return fmt.Errorf("pattern dimensions must be positive and finite")
|
||||
}
|
||||
}
|
||||
for _, step := range []*float64{style.XStep, style.YStep} {
|
||||
if step != nil && (!finite(*step) || *step < 0) {
|
||||
return fmt.Errorf("pattern steps must be nonnegative and finite")
|
||||
}
|
||||
}
|
||||
if style.CTM != nil && *style.CTM != "" {
|
||||
if _, err := creationNumbers(*style.CTM, 6); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
objects, _, err := e.selectedObjects(page, ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range objects {
|
||||
objects[i] = cloneEditorData(objects[i])
|
||||
object := &objects[i]
|
||||
var fill *FillColor
|
||||
switch object.Type {
|
||||
case "TextObject":
|
||||
fill = object.TextObject.FillColor
|
||||
if stroke {
|
||||
fill = (*FillColor)(object.TextObject.StrokeColor)
|
||||
}
|
||||
case "PathObject", "Path":
|
||||
fill = object.PathObject.FillColor
|
||||
if stroke {
|
||||
fill = (*FillColor)(object.PathObject.StrokeColor)
|
||||
}
|
||||
}
|
||||
if fill == nil || fill.Pattern == nil {
|
||||
return fmt.Errorf("object %q does not use a pattern", ids[i])
|
||||
}
|
||||
pattern := fill.Pattern
|
||||
for _, pair := range []struct{ value, target *float64 }{{style.Width, &pattern.Width}, {style.Height, &pattern.Height}, {style.XStep, &pattern.XStep}, {style.YStep, &pattern.YStep}} {
|
||||
if pair.value != nil {
|
||||
*pair.target = *pair.value
|
||||
}
|
||||
}
|
||||
if style.CTM != nil {
|
||||
pattern.CTM = *style.CTM
|
||||
}
|
||||
}
|
||||
return e.updateObjects(page, objects, true)
|
||||
}
|
||||
|
||||
// Color 解析当前文档的纯色,支持RGB、灰度、CMYK及调色板,不修改原颜色
|
||||
// 入参: value 颜色,nil表示默认黑色
|
||||
// 返回: color.NRGBA 非预乘颜色, error 错误信息
|
||||
@@ -141,9 +205,42 @@ func (e *Editor) RGBColor(value color.NRGBA) (*FillColor, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// PaintObjects 更新文字或路径的填充或描边,保留定位、其他样式和原文扩展
|
||||
// 入参: page 页面索引, ids 对象标识, paint 绘制颜色,nil恢复默认, stroke 是否修改描边
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) PaintObjects(page int, ids []string, paint *FillColor, stroke bool) error {
|
||||
if err := e.editorColor(paint); err != nil {
|
||||
return err
|
||||
}
|
||||
objects, _, err := e.selectedObjects(page, ids)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range objects {
|
||||
object := &objects[i]
|
||||
switch object.Type {
|
||||
case "TextObject":
|
||||
if stroke {
|
||||
object.TextObject.StrokeColor = (*StrokeColor)(cloneEditorData(paint))
|
||||
} else {
|
||||
object.TextObject.FillColor = cloneEditorData(paint)
|
||||
}
|
||||
case "PathObject":
|
||||
if stroke {
|
||||
object.PathObject.StrokeColor = (*StrokeColor)(cloneEditorData(paint))
|
||||
} else {
|
||||
object.PathObject.FillColor = cloneEditorData(paint)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("object %q does not have text or path paint", editorObjectID(*object))
|
||||
}
|
||||
}
|
||||
return e.updateObjects(page, objects, true)
|
||||
}
|
||||
|
||||
// editorPaintable 判断文字与路径颜色能否在保留其他字段的前提下修改
|
||||
// 入参: object 图形对象
|
||||
// 返回: bool 是否支持纯色编辑
|
||||
// 返回: bool 是否支持绘制颜色编辑
|
||||
func (e *Editor) editorPaintable(object GraphicObject) bool {
|
||||
var fill, stroke *FillColor
|
||||
switch object.Type {
|
||||
@@ -155,17 +252,111 @@ func (e *Editor) editorPaintable(object GraphicObject) bool {
|
||||
return false
|
||||
}
|
||||
for _, value := range []*FillColor{fill, stroke} {
|
||||
if _, err := e.Color(value); err != nil {
|
||||
if err := e.editorColor(value); err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// editorColor 校验当前文档的颜色空间和分量
|
||||
// collectPaintReferences 收集纯色、渐变和图案引用的资源
|
||||
// 入参: paint 绘制颜色, used 引用集合
|
||||
func collectPaintReferences(paint *FillColor, used map[string]bool) {
|
||||
if paint == nil {
|
||||
return
|
||||
}
|
||||
used[paint.ColorSpace] = true
|
||||
var segments []ShdSegment
|
||||
if paint.AxialShd != nil {
|
||||
segments = paint.AxialShd.Segment
|
||||
}
|
||||
if paint.RadialShd != nil {
|
||||
segments = paint.RadialShd.Segment
|
||||
}
|
||||
for _, segment := range segments {
|
||||
used[segment.Color.ColorSpace] = true
|
||||
}
|
||||
if paint.Pattern != nil {
|
||||
for _, object := range paint.Pattern.CellContent.Objects {
|
||||
collectObjectReferences(object, used)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collectObjectReferences 收集对象和嵌套绘制内容引用的资源
|
||||
// 入参: object 图形对象, used 引用集合
|
||||
func collectObjectReferences(object GraphicObject, used map[string]bool) {
|
||||
switch object.Type {
|
||||
case "TextObject":
|
||||
used[object.TextObject.Font], used[object.TextObject.DrawParam] = true, true
|
||||
collectPaintReferences(object.TextObject.FillColor, used)
|
||||
collectPaintReferences((*FillColor)(object.TextObject.StrokeColor), used)
|
||||
case "PathObject", "Path":
|
||||
used[object.PathObject.DrawParam] = true
|
||||
collectPaintReferences(object.PathObject.FillColor, used)
|
||||
collectPaintReferences((*FillColor)(object.PathObject.StrokeColor), used)
|
||||
case "ImageObject":
|
||||
used[object.ImageObject.ResourceID], used[object.ImageObject.ImageMask] = true, true
|
||||
if object.ImageObject.Border != nil {
|
||||
collectPaintReferences((*FillColor)(object.ImageObject.Border.BorderColor), used)
|
||||
}
|
||||
case "CompositeObject", "CompositeGraphicUnit":
|
||||
collectCompositeReferences(object.CompositeGraphicUnit, used)
|
||||
}
|
||||
}
|
||||
|
||||
// editorColor 校验当前文档的纯色和渐变
|
||||
// 入参: value 颜色
|
||||
// 返回: error 错误信息
|
||||
func (e *Editor) editorColor(value *FillColor) error {
|
||||
_, err := e.Color(value)
|
||||
return err
|
||||
if value == nil || value.AxialShd == nil && value.RadialShd == nil {
|
||||
_, err := e.Color(value)
|
||||
return err
|
||||
}
|
||||
if value.unsupported || value.Pattern != nil || value.AxialShd != nil && value.RadialShd != nil || value.Value != "" || value.Index != nil {
|
||||
return fmt.Errorf("specify one paint source")
|
||||
}
|
||||
if value.Alpha != nil && (*value.Alpha < 0 || *value.Alpha > 255) {
|
||||
return fmt.Errorf("color alpha must be between 0 and 255")
|
||||
}
|
||||
var mapType, extend, start, end string
|
||||
var unit float64
|
||||
var segments []ShdSegment
|
||||
if node := value.AxialShd; node != nil {
|
||||
mapType, unit, extend, start, end, segments = node.MapType, node.MapUnit, node.Extend, node.StartPoint, node.EndPoint, node.Segment
|
||||
} else {
|
||||
node := value.RadialShd
|
||||
mapType, unit, extend, start, end, segments = node.MapType, node.MapUnit, node.Extend, node.StartPoint, node.EndPoint, node.Segment
|
||||
if !finite(node.StartRadius) || !finite(node.EndRadius) || node.StartRadius < 0 || node.EndRadius < 0 || !finite(node.Eccentricity) || node.Eccentricity < 0 || node.Eccentricity >= 1 || !finite(node.Angle) {
|
||||
return fmt.Errorf("invalid radial gradient geometry")
|
||||
}
|
||||
}
|
||||
if !slices.Contains([]string{"", "Direct", "Repeat", "Reflect"}, mapType) || !finite(unit) || unit < 0 || (mapType == "Repeat" || mapType == "Reflect") && unit == 0 || !slices.Contains([]string{"", "0", "1", "2", "3"}, extend) {
|
||||
return fmt.Errorf("invalid gradient mapping")
|
||||
}
|
||||
for _, point := range []string{start, end} {
|
||||
if _, err := creationNumbers(point, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(segments) < 2 {
|
||||
return fmt.Errorf("gradient requires at least two stops")
|
||||
}
|
||||
previous := -1.0
|
||||
positions := gradientPositions(segments)
|
||||
for index, segment := range segments {
|
||||
position := positions[index]
|
||||
if !finite(position) || position < previous || position < 0 || position > 1 {
|
||||
return fmt.Errorf("gradient stops must be ordered within zero and one")
|
||||
}
|
||||
previous = position
|
||||
color := segment.Color
|
||||
if color.ColorSpace == "" {
|
||||
color.ColorSpace = value.ColorSpace
|
||||
}
|
||||
if _, err := e.Color(&FillColor{Value: color.Value, Index: color.Index, ColorSpace: color.ColorSpace, Alpha: color.Alpha}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user