diff --git a/ofdgo_object.go b/ofdgo_object.go index eb97cd9..aaede3c 100644 --- a/ofdgo_object.go +++ b/ofdgo_object.go @@ -237,6 +237,15 @@ func (p *PatternContent) decodeObject(d *xml.Decoder, start xml.StartElement) er return d.Skip() } +// UnmarshalXML 解析渐变分段并保留位置的缺省状态 +// 入参: d XML解码器, start 起始节点 +// 返回: error 错误信息 +func (s *ShdSegment) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type segment ShdSegment + *s = ShdSegment{positionMissing: attrValue(start, "Position") == ""} + return d.DecodeElement((*segment)(s), &start) +} + // attrValue 获取XML属性值 // 入参: start 起始节点, name 属性名 // 返回: string 属性值 diff --git a/ofdgo_page.go b/ofdgo_page.go index b2ac3e0..012f5d0 100644 --- a/ofdgo_page.go +++ b/ofdgo_page.go @@ -114,12 +114,13 @@ type FillColor struct { // Pattern 图案填充 type Pattern struct { - Width float64 `xml:"Width,attr"` - Height float64 `xml:"Height,attr"` - XStep float64 `xml:"XStep,attr"` - YStep float64 `xml:"YStep,attr"` - CTM string `xml:"CTM,attr"` - CellContent PatternContent `xml:"CellContent"` + Width float64 `xml:"Width,attr"` + Height float64 `xml:"Height,attr"` + XStep float64 `xml:"XStep,attr"` + YStep float64 `xml:"YStep,attr"` + ReflectMethod string `xml:"ReflectMethod,attr"` + CTM string `xml:"CTM,attr"` + CellContent PatternContent `xml:"CellContent"` } // PatternContent 图案单元内容 @@ -201,8 +202,9 @@ type RadialShd struct { // ShdSegment 渐变分段 type ShdSegment struct { - Position float64 `xml:"Position,attr"` - Color ShdColor `xml:"Color"` + Position float64 `xml:"Position,attr"` + Color ShdColor `xml:"Color"` + positionMissing bool } // ShdColor 渐变颜色 diff --git a/ofdgo_paint.go b/ofdgo_paint.go index b8cb5b0..70e1806 100644 --- a/ofdgo_paint.go +++ b/ofdgo_paint.go @@ -257,7 +257,26 @@ func parseShdColor(segments []ShdSegment, alpha *int) color.Color { // 返回: canvas.Grad 渐变分段 func parseShdSegments(segments []ShdSegment, alpha *int) canvas.Grad { gradient := canvas.NewGradient() - for _, segment := range segments { + position, step := 0.0, 0.0 + for i, segment := range segments { + if !segment.positionMissing { + position = segment.Position + } else if i == len(segments)-1 { + position = 1 + } + if i == 0 || !segment.positionMissing { + next := i + 1 + for next < len(segments)-1 && segments[next].positionMissing { + next++ + } + end := 1.0 + if next < len(segments) && !segments[next].positionMissing { + end = segments[next].Position + } + step = (end - position) / float64(next-i) + } + offset := position + position += step if strings.TrimSpace(segment.Color.Value) == "" { continue } @@ -265,7 +284,7 @@ func parseShdSegments(segments []ShdSegment, alpha *int) canvas.Grad { if segmentAlpha == nil { segmentAlpha = segment.Color.Alpha } - gradient.Add(segment.Position, colorToRGBA(parseColorWithAlpha(segment.Color.Value, segmentAlpha))) + gradient.Add(offset, colorToRGBA(parseColorWithAlpha(segment.Color.Value, segmentAlpha))) } if len(gradient) == 0 { return nil diff --git a/ofdgo_renderer_path.go b/ofdgo_renderer_path.go index f5b6d69..bbc7c5b 100644 --- a/ofdgo_renderer_path.go +++ b/ofdgo_renderer_path.go @@ -188,10 +188,20 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64 } style.applyPathObject(obj, bx, by, pageH) style.scale(ctm) - p := r.buildPath(obj, pageH, ctm, boundaryInCTM) - if rectPath := r.buildTinyFillRectPath(obj, pageH, ctm, bx, by); rectPath != nil { + pathCTM := ctm + if boundaryInCTM { + pathCTM = localCTM + } + p := r.buildPath(obj, pageH, pathCTM, false) + if rectPath := r.buildTinyFillRectPath(obj, pageH, pathCTM, bx, by); rectPath != nil { p = rectPath } + if boundaryInCTM && parentCTM != nil { + p = p.Transform(canvas.Matrix{ + {parentCTM.a, -parentCTM.c, parentCTM.c*pageH + parentCTM.e}, + {-parentCTM.b, parentCTM.d, pageH*(1-parentCTM.d) - parentCTM.f}, + }) + } clipPath := intersectClipPath(parentClip, r.buildObjectClipPath(obj.Clips, pageH, bx, by, localCTM, parentCTM, boundaryInCTM)) shouldFill := false if obj.Fill != nil { @@ -309,8 +319,14 @@ func (r *Renderer) renderPattern(ctx *canvas.Context, pattern *Pattern, defaultC for ix := startX; ix <= endX; ix++ { for iy := startY; iy <= endY; iy++ { tileCTM := patternCTM.Multiply(TranslationMatrix(float64(ix)*xStep, float64(iy)*yStep)) + if ix%2 != 0 && (pattern.ReflectMethod == "Column" || pattern.ReflectMethod == "RowAndColumn") { + tileCTM = tileCTM.Multiply(Matrix{a: -1, d: 1, e: pattern.Width}) + } + if iy%2 != 0 && (pattern.ReflectMethod == "Row" || pattern.ReflectMethod == "RowAndColumn") { + tileCTM = tileCTM.Multiply(Matrix{a: 1, d: -1, f: pattern.Height}) + } for _, obj := range pattern.CellContent.Objects { - r.renderObject(ctx, obj, pageH, defaultColor, defaultColor, 0, &tileCTM, false, clip) + r.renderObject(ctx, obj, pageH, defaultColor, defaultColor, 0, &tileCTM, true, clip) } } }