feat(渲染能力): 完善底纹填充、描边及基础颜色空间支持
build.yaml / build (push) Successful in 36s

This commit is contained in:
xiaoqidun committed 2026-09-09 19:01:50 +08:00
1 parent 6c9b3f6019
commit 777d0c7c89
8 files changed
+228 -153

No files matched your search

+24
View File
@@ -19,6 +19,30 @@ import (
"strconv"
)
// UnmarshalXML 解析填充颜色并区分未支持的复杂颜色
// 入参: d XML解码器, start 起始节点
// 返回: error 错误信息
func (c *FillColor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
type plain FillColor
var value struct {
plain
Other *struct{} `xml:",any"`
}
if err := d.DecodeElement(&value, &start); err != nil {
return err
}
*c = FillColor(value.plain)
c.unsupported = value.Other != nil
return nil
}
// UnmarshalXML 解析勾边颜色并区分未支持的复杂颜色
// 入参: d XML解码器, start 起始节点
// 返回: error 错误信息
func (c *StrokeColor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
return (*FillColor)(c).UnmarshalXML(d, start)
}
// graphicObjectTarget 图形对象集合
type graphicObjectTarget struct {
objects *[]GraphicObject
+8 -6
View File
@@ -22,6 +22,7 @@ type PageContent struct {
ID string `xml:"-"`
Area PageArea `xml:"Area"`
Template []Template `xml:"Template"`
PageRes string `xml:"PageRes"`
Content Content `xml:"Content"`
Actions []Action `xml:"Actions>Action"`
}
@@ -106,10 +107,13 @@ type TextObject struct {
// FillColor 填充颜色
type FillColor struct {
Value string `xml:"Value,attr"`
Index *int `xml:"Index,attr"`
ColorSpace string `xml:"ColorSpace,attr"`
Alpha *int `xml:"Alpha,attr"`
Pattern *Pattern `xml:"Pattern"`
AxialShd *AxialShd `xml:"AxialShd"`
RadialShd *RadialShd `xml:"RadialShd"`
unsupported bool
}
// Pattern 图案填充
@@ -119,6 +123,7 @@ type Pattern struct {
XStep float64 `xml:"XStep,attr"`
YStep float64 `xml:"YStep,attr"`
ReflectMethod string `xml:"ReflectMethod,attr"`
RelativeTo string `xml:"RelativeTo,attr"`
CTM string `xml:"CTM,attr"`
CellContent PatternContent `xml:"CellContent"`
}
@@ -175,12 +180,7 @@ type PathObject struct {
}
// StrokeColor 勾边颜色
type StrokeColor struct {
Value string `xml:"Value,attr"`
Alpha *int `xml:"Alpha,attr"`
AxialShd *AxialShd `xml:"AxialShd"`
RadialShd *RadialShd `xml:"RadialShd"`
}
type StrokeColor FillColor
// AxialShd 轴向渐变
type AxialShd struct {
@@ -216,6 +216,8 @@ type ShdSegment struct {
// ShdColor 渐变颜色
type ShdColor struct {
Value string `xml:"Value,attr"`
Index *int `xml:"Index,attr"`
ColorSpace string `xml:"ColorSpace,attr"`
Alpha *int `xml:"Alpha,attr"`
}
+87 -100
View File
@@ -24,40 +24,66 @@ import (
)
// parseColorWithAlpha 解析带透明度的颜色
// 入参: val 颜色值, alpha 透明度
// 入参: value 颜色值, index 调色板索引, space 颜色空间标识, alpha 透明度
// 返回: color.Color 颜色对象
func parseColorWithAlpha(val string, alpha *int) color.Color {
parts := strings.Fields(val)
if len(parts) >= 3 {
r := parseColorComponent(parts[0])
g := parseColorComponent(parts[1])
b := parseColorComponent(parts[2])
func (r *Renderer) parseColorWithAlpha(value string, index *int, space string, alpha *int) color.Color {
if space == "" && r.Reader.doc != nil {
space = strconv.Itoa(r.Reader.doc.CommonData.DefaultCS)
}
cs := r.Reader.colorSpaceCache[space]
kind, bits, count := "RGB", 8, 3
if cs != nil {
kind = cs.Type
switch cs.BitsPerComponent {
case 1, 2, 4, 8, 16:
bits = cs.BitsPerComponent
}
if strings.TrimSpace(value) == "" && index != nil && 0 <= *index && *index < len(cs.Palette) {
value = cs.Palette[*index]
}
}
switch kind {
case "GRAY":
count = 1
case "CMYK":
count = 4
}
parts := strings.Fields(value)
var components [4]uint8
limit := 1<<bits - 1
if len(parts) >= count {
for i, part := range parts[:count] {
v, err := parseColorComponent(part)
if err != nil || v < 0 || limit < v {
components = [4]uint8{}
break
}
components[i] = uint8((v*255 + limit/2) / limit)
}
}
red, green, blue := components[0], components[1], components[2]
switch kind {
case "GRAY":
green, blue = red, red
case "CMYK":
red, green, blue = color.CMYKToRGB(components[0], components[1], components[2], components[3])
}
a := 255
if alpha != nil {
a = *alpha
a = clampColor(*alpha)
}
a = clampColor(a)
return color.RGBA{
R: uint8(clampColor(r) * a / 255),
G: uint8(clampColor(g) * a / 255),
B: uint8(clampColor(b) * a / 255),
A: uint8(a),
}
}
return color.Black
return color.RGBA{R: uint8(int(red) * a / 255), G: uint8(int(green) * a / 255), B: uint8(int(blue) * a / 255), A: uint8(a)}
}
// parseColorComponent 解析颜色分量
// 入参: s 颜色分量
// 返回: int 颜色分量值
func parseColorComponent(s string) int {
s = strings.TrimSpace(s)
// 返回: int 颜色分量值, error 错误信息
func parseColorComponent(s string) (int, error) {
if strings.HasPrefix(s, "#") {
v, _ := strconv.ParseInt(strings.TrimPrefix(s, "#"), 16, 0)
return int(v)
v, err := strconv.ParseInt(s[1:], 16, 0)
return int(v), err
}
v, _ := strconv.Atoi(s)
return v
return strconv.Atoi(s)
}
// clampColor 限制颜色分量范围
@@ -135,7 +161,7 @@ func colorWithAlpha(c color.Color, alpha *int) color.Color {
// parseFillColor 解析填充颜色
// 入参: fillColor 填充颜色节点
// 返回: color.Color 颜色对象
func parseFillColor(fillColor *FillColor) color.Color {
func (r *Renderer) parseFillColor(fillColor *FillColor) color.Color {
if fillColor == nil {
return nil
}
@@ -143,117 +169,81 @@ func parseFillColor(fillColor *FillColor) color.Color {
return nil
}
if fillColor.AxialShd != nil {
return parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha)
return r.parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha)
}
if fillColor.RadialShd != nil {
return parseShdColor(fillColor.RadialShd.Segment, fillColor.Alpha)
}
if strings.TrimSpace(fillColor.Value) != "" {
return parseColorWithAlpha(fillColor.Value, fillColor.Alpha)
return r.parseShdColor(fillColor.RadialShd.Segment, fillColor.Alpha)
}
if fillColor.unsupported && strings.TrimSpace(fillColor.Value) == "" && fillColor.Index == nil {
return nil
}
return r.parseColorWithAlpha(fillColor.Value, fillColor.Index, fillColor.ColorSpace, fillColor.Alpha)
}
// parseFillPaint 解析填充画刷
// 入参: fillColor 填充颜色节点, x X坐标, y Y坐标, pageH 页面高度
// 返回: any 填充画刷
func parseFillPaint(fillColor *FillColor, x, y, pageH float64) any {
func (r *Renderer) parseFillPaint(fillColor *FillColor, x, y, pageH float64) any {
if fillColor == nil {
return nil
}
if fillColor.Pattern != nil {
return nil
}
if gradient := parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
if gradient := r.parseAxialShdGradient(fillColor.AxialShd, fillColor.Alpha, x, y, pageH); gradient != nil {
return newShdPaint(gradient, fillColor.AxialShd.Extend, fillColor.AxialShd.MapType, fillColor.AxialShd.MapUnit)
}
if paint := parseRadialShdPaint(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); paint != nil {
if paint := r.parseRadialShdPaint(fillColor.RadialShd, fillColor.Alpha, x, y, pageH); paint != nil {
return paint
}
if fillColor.AxialShd != nil {
return parseShdColor(fillColor.AxialShd.Segment, fillColor.Alpha)
}
if fillColor.RadialShd != nil {
return parseShdColor(fillColor.RadialShd.Segment, fillColor.Alpha)
}
if strings.TrimSpace(fillColor.Value) != "" {
return parseColorWithAlpha(fillColor.Value, fillColor.Alpha)
}
return nil
return r.parseFillColor(fillColor)
}
// parseStrokeColor 解析勾边颜色
// 入参: strokeColor 勾边颜色节点
// 返回: color.Color 颜色对象
func parseStrokeColor(strokeColor *StrokeColor) color.Color {
if strokeColor == nil {
return nil
}
if strokeColor.AxialShd != nil {
return parseShdColor(strokeColor.AxialShd.Segment, strokeColor.Alpha)
}
if strokeColor.RadialShd != nil {
return parseShdColor(strokeColor.RadialShd.Segment, strokeColor.Alpha)
}
if strings.TrimSpace(strokeColor.Value) != "" {
return parseColorWithAlpha(strokeColor.Value, strokeColor.Alpha)
}
return nil
func (r *Renderer) parseStrokeColor(strokeColor *StrokeColor) color.Color {
return r.parseFillColor((*FillColor)(strokeColor))
}
// parseStrokePaint 解析勾边画刷
// 入参: strokeColor 勾边颜色节点, x X坐标, y Y坐标, pageH 页面高度
// 返回: any 勾边画刷
func parseStrokePaint(strokeColor *StrokeColor, x, y, pageH float64) any {
if strokeColor == nil {
return nil
}
if gradient := parseAxialShdGradient(strokeColor.AxialShd, strokeColor.Alpha, x, y, pageH); gradient != nil {
return newShdPaint(gradient, strokeColor.AxialShd.Extend, strokeColor.AxialShd.MapType, strokeColor.AxialShd.MapUnit)
}
if paint := parseRadialShdPaint(strokeColor.RadialShd, strokeColor.Alpha, x, y, pageH); paint != nil {
return paint
}
if strokeColor.AxialShd != nil {
return parseShdColor(strokeColor.AxialShd.Segment, strokeColor.Alpha)
}
if strokeColor.RadialShd != nil {
return parseShdColor(strokeColor.RadialShd.Segment, strokeColor.Alpha)
}
if strings.TrimSpace(strokeColor.Value) != "" {
return parseColorWithAlpha(strokeColor.Value, strokeColor.Alpha)
}
return nil
func (r *Renderer) parseStrokePaint(strokeColor *StrokeColor, x, y, pageH float64) any {
return r.parseFillPaint((*FillColor)(strokeColor), x, y, pageH)
}
// patternColor 获取图案单元默认颜色
// 入参: fillColor 填充颜色节点
// 返回: color.Color 默认颜色
func patternColor(fillColor *FillColor) color.Color {
if fillColor == nil || strings.TrimSpace(fillColor.Value) == "" {
type patternPaint struct {
*Pattern
color color.Color
alpha *int
}
// parsePatternPaint 解析底纹画刷
// 入参: pattern 底纹对象, value 颜色值, index 调色板索引, space 颜色空间标识, alpha 透明度
// 返回: *patternPaint 底纹画刷
func (r *Renderer) parsePatternPaint(pattern *Pattern, value string, index *int, space string, alpha *int) *patternPaint {
if pattern == nil {
return nil
}
return parseColorWithAlpha(fillColor.Value, fillColor.Alpha)
return &patternPaint{Pattern: pattern, color: r.parseColorWithAlpha(value, index, space, nil), alpha: alpha}
}
// parseShdColor 解析渐变颜色
// 入参: segments 渐变分段, alpha 透明度
// 返回: color.Color 颜色对象
func parseShdColor(segments []ShdSegment, alpha *int) color.Color {
for _, segment := range segments {
if strings.TrimSpace(segment.Color.Value) == "" {
continue
}
segmentAlpha := mergeAlpha(segment.Color.Alpha, alpha)
return parseColorWithAlpha(segment.Color.Value, segmentAlpha)
}
func (r *Renderer) parseShdColor(segments []ShdSegment, alpha *int) color.Color {
if len(segments) == 0 {
return color.Black
}
c := segments[0].Color
return r.parseColorWithAlpha(c.Value, c.Index, c.ColorSpace, mergeAlpha(c.Alpha, alpha))
}
// parseShdSegments 解析渐变分段
// 入参: segments 渐变分段, alpha 透明度
// 返回: canvas.Grad 渐变分段
func parseShdSegments(segments []ShdSegment, alpha *int) canvas.Grad {
func (r *Renderer) parseShdSegments(segments []ShdSegment, alpha *int) canvas.Grad {
gradient := canvas.NewGradient()
position, step := 0.0, 0.0
for i, segment := range segments {
@@ -275,11 +265,8 @@ func parseShdSegments(segments []ShdSegment, alpha *int) canvas.Grad {
}
offset := position
position += step
if strings.TrimSpace(segment.Color.Value) == "" {
continue
}
segmentAlpha := mergeAlpha(segment.Color.Alpha, alpha)
gradient.Add(offset, colorToRGBA(parseColorWithAlpha(segment.Color.Value, segmentAlpha)))
gradient.Add(offset, colorToRGBA(r.parseColorWithAlpha(segment.Color.Value, segment.Color.Index, segment.Color.ColorSpace, segmentAlpha)))
}
if len(gradient) == 0 {
return nil
@@ -290,7 +277,7 @@ func parseShdSegments(segments []ShdSegment, alpha *int) canvas.Grad {
// parseAxialShdGradient 解析轴向渐变
// 入参: axialShd 轴向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度
// 返回: canvas.Gradient 渐变对象
func parseAxialShdGradient(axialShd *AxialShd, alpha *int, x, y, pageH float64) canvas.Gradient {
func (r *Renderer) parseAxialShdGradient(axialShd *AxialShd, alpha *int, x, y, pageH float64) canvas.Gradient {
if axialShd == nil {
return nil
}
@@ -299,7 +286,7 @@ func parseAxialShdGradient(axialShd *AxialShd, alpha *int, x, y, pageH float64)
if len(start) < 2 || len(end) < 2 {
return nil
}
gradient := parseShdSegments(axialShd.Segment, alpha)
gradient := r.parseShdSegments(axialShd.Segment, alpha)
if gradient == nil {
return nil
}
@@ -342,7 +329,7 @@ func axialShdClip(ctx *canvas.Context, gradient *canvas.LinearGradient, extend i
// parseRadialShdPaint 解析径向渐变画刷
// 入参: radialShd 径向渐变节点, alpha 透明度, x X坐标, y Y坐标, pageH 页面高度
// 返回: *shdPaint 渐变画刷
func parseRadialShdPaint(radialShd *RadialShd, alpha *int, x, y, pageH float64) *shdPaint {
func (r *Renderer) parseRadialShdPaint(radialShd *RadialShd, alpha *int, x, y, pageH float64) *shdPaint {
if radialShd == nil || radialShd.EndRadius <= 0 {
return nil
}
@@ -351,7 +338,7 @@ func parseRadialShdPaint(radialShd *RadialShd, alpha *int, x, y, pageH float64)
if len(start) < 2 || len(end) < 2 {
return nil
}
gradient := parseShdSegments(radialShd.Segment, alpha)
gradient := r.parseShdSegments(radialShd.Segment, alpha)
if gradient == nil {
return nil
}
+9
View File
@@ -32,6 +32,7 @@ type Reader struct {
RootDir string
ResMap map[string]string
fontCache map[string]*Font
colorSpaceCache map[string]*ColorSpace
drawParamCache map[string]*DrawParam
compositeGraphicUnitCache map[string]*CompositeGraphicUnit
doc *Document
@@ -74,6 +75,7 @@ func (r *Reader) initRoot() error {
r.OFD = &ofd
r.ResMap = make(map[string]string)
r.fontCache = make(map[string]*Font)
r.colorSpaceCache = make(map[string]*ColorSpace)
r.drawParamCache = make(map[string]*DrawParam)
r.compositeGraphicUnitCache = make(map[string]*CompositeGraphicUnit)
return nil
@@ -186,6 +188,10 @@ func (r *Reader) loadRes(resPath string) {
return
}
baseLoc := res.BaseLoc
for i := range res.ColorSpaces.ColorSpace {
cs := &res.ColorSpaces.ColorSpace[i]
r.colorSpaceCache[cs.ID] = cs
}
for _, mm := range res.MultiMedias.MultiMedia {
if mm.MediaFile != "" {
if finalPath := resolveResourcePath(resPath, baseLoc, mm.MediaFile); finalPath != "" {
@@ -245,6 +251,9 @@ func (r *Reader) PageContent(page Page) (*PageContent, error) {
return nil, fmt.Errorf("failed to unmarshal page content: %w", err)
}
content.ID = page.ID
if content.PageRes != "" {
r.loadRes(resolveResourcePath(page.BaseLoc, "", content.PageRes))
}
return &content, nil
}
+2 -2
View File
@@ -194,10 +194,10 @@ func (r *Renderer) drawParamDefaults(id string, defaultFill, defaultStroke color
defaultLW = dp.LineWidth
}
if dp.FillColor != nil {
defaultFill = parseFillColor(dp.FillColor)
defaultFill = r.parseFillColor(dp.FillColor)
}
if dp.StrokeColor != nil {
defaultStroke = parseStrokeColor(dp.StrokeColor)
defaultStroke = r.parseStrokeColor(dp.StrokeColor)
}
return defaultFill, defaultStroke, defaultLW
}
+43 -31
View File
@@ -30,8 +30,8 @@ type pathStyle struct {
strokeColor color.Color
fillPaint any
strokePaint any
fillPattern *Pattern
fillPatternColor color.Color
fillPattern *patternPaint
strokePattern *patternPaint
lineWidth float64
lineCap canvas.Capper
lineJoin canvas.Joiner
@@ -59,21 +59,21 @@ func newPathStyle(defaultFill, defaultStroke color.Color, defaultLW float64, alp
}
// applyFillColor 应用填充颜色
// 入参: fill 填充颜色, bx 边界X坐标, by 边界Y坐标, pageH 页面高度, alpha 对象透明度
func (s *pathStyle) applyFillColor(fill *FillColor, bx, by, pageH float64, alpha *int) {
// 入参: r 渲染器, fill 填充颜色, bx 边界X坐标, by 边界Y坐标, pageH 页面高度, alpha 对象透明度
func (s *pathStyle) applyFillColor(r *Renderer, fill *FillColor, bx, by, pageH float64, alpha *int) {
fillColorNode := withFillAlpha(fill, alpha)
s.fillPattern = fillColorNode.Pattern
s.fillPatternColor = patternColor(fillColorNode)
s.fillColor = parseFillColor(fillColorNode)
s.fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
s.fillPattern = r.parsePatternPaint(fillColorNode.Pattern, fillColorNode.Value, fillColorNode.Index, fillColorNode.ColorSpace, fillColorNode.Alpha)
s.fillColor = r.parseFillColor(fillColorNode)
s.fillPaint = r.parseFillPaint(fillColorNode, bx, by, pageH)
}
// applyStrokeColor 应用描边颜色
// 入参: stroke 描边颜色, bx 边界X坐标, by 边界Y坐标, pageH 页面高度, alpha 对象透明度
func (s *pathStyle) applyStrokeColor(stroke *StrokeColor, bx, by, pageH float64, alpha *int) {
// 入参: r 渲染器, stroke 描边颜色, bx 边界X坐标, by 边界Y坐标, pageH 页面高度, alpha 对象透明度
func (s *pathStyle) applyStrokeColor(r *Renderer, stroke *StrokeColor, bx, by, pageH float64, alpha *int) {
strokeColorNode := withStrokeAlpha(stroke, alpha)
s.strokeColor = parseStrokeColor(strokeColorNode)
s.strokePaint = parseStrokePaint(strokeColorNode, bx, by, pageH)
s.strokePattern = r.parsePatternPaint(strokeColorNode.Pattern, strokeColorNode.Value, strokeColorNode.Index, strokeColorNode.ColorSpace, strokeColorNode.Alpha)
s.strokeColor = r.parseStrokeColor(strokeColorNode)
s.strokePaint = r.parseStrokePaint(strokeColorNode, bx, by, pageH)
}
// pathLineCap 转换线帽样式
@@ -103,16 +103,16 @@ func pathLineJoin(join string, fallback canvas.Joiner) canvas.Joiner {
}
// applyDrawParam 应用绘制参数样式
// 入参: dp 绘制参数, bx 边界X坐标, by 边界Y坐标, pageH 页面高度, alpha 对象透明度
func (s *pathStyle) applyDrawParam(dp *DrawParam, bx, by, pageH float64, alpha *int) {
// 入参: r 渲染器, dp 绘制参数, bx 边界X坐标, by 边界Y坐标, pageH 页面高度, alpha 对象透明度
func (s *pathStyle) applyDrawParam(r *Renderer, dp *DrawParam, bx, by, pageH float64, alpha *int) {
if dp.LineWidth > 0 {
s.lineWidth = dp.LineWidth
}
if dp.FillColor != nil {
s.applyFillColor(dp.FillColor, bx, by, pageH, alpha)
s.applyFillColor(r, dp.FillColor, bx, by, pageH, alpha)
}
if dp.StrokeColor != nil {
s.applyStrokeColor(dp.StrokeColor, bx, by, pageH, alpha)
s.applyStrokeColor(r, dp.StrokeColor, bx, by, pageH, alpha)
}
if dp.Cap != "" {
s.lineCap = pathLineCap(dp.Cap, s.lineCap)
@@ -127,16 +127,16 @@ func (s *pathStyle) applyDrawParam(dp *DrawParam, bx, by, pageH float64, alpha *
}
// applyPathObject 应用路径对象样式
// 入参: obj 路径对象, bx 边界X坐标, by 边界Y坐标, pageH 页面高度
func (s *pathStyle) applyPathObject(obj PathObject, bx, by, pageH float64) {
// 入参: r 渲染器, obj 路径对象, bx 边界X坐标, by 边界Y坐标, pageH 页面高度
func (s *pathStyle) applyPathObject(r *Renderer, obj PathObject, bx, by, pageH float64) {
if obj.LineWidth > 0 {
s.lineWidth = obj.LineWidth
}
if obj.FillColor != nil {
s.applyFillColor(obj.FillColor, bx, by, pageH, obj.Alpha)
s.applyFillColor(r, obj.FillColor, bx, by, pageH, obj.Alpha)
}
if obj.StrokeColor != nil {
s.applyStrokeColor(obj.StrokeColor, bx, by, pageH, obj.Alpha)
s.applyStrokeColor(r, obj.StrokeColor, bx, by, pageH, obj.Alpha)
}
if obj.Cap != "" {
s.lineCap = pathLineCap(obj.Cap, canvas.ButtCap)
@@ -183,11 +183,15 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
style := newPathStyle(defaultFill, defaultStroke, defaultLW, obj.Alpha)
if obj.DrawParam != "" {
if dp := r.getDrawParam(obj.DrawParam, nil); dp != nil {
style.applyDrawParam(dp, bx, by, pageH, obj.Alpha)
style.applyDrawParam(r, dp, bx, by, pageH, obj.Alpha)
}
}
style.applyPathObject(obj, bx, by, pageH)
style.applyPathObject(r, obj, bx, by, pageH)
style.scale(ctm)
patternCTM := TranslationMatrix(bx, by).Multiply(ctm)
if boundaryInCTM && parentCTM != nil {
patternCTM = parentCTM.Multiply(TranslationMatrix(bx, by)).Multiply(localCTM)
}
pathCTM := ctm
if boundaryInCTM {
pathCTM = localCTM
@@ -227,7 +231,7 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
fp = applyClipPath(fp, fillClip)
}
if style.fillPattern != nil {
r.renderPattern(ctx, style.fillPattern, style.fillPatternColor, pageH, fp, ctm, bx, by)
r.renderPattern(ctx, style.fillPattern, pageH, fp, patternCTM)
} else if style.fillPaint != nil {
ctx.SetFillRule(fillRule)
ctx.SetFill(fillPaint)
@@ -259,13 +263,17 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
ctx.SetDashes(0)
}
strokeClip := intersectClipPath(clipPath, shadingClip)
if strokeClip != nil || strokeView != canvas.Identity {
if strokeClip != nil || strokeView != canvas.Identity || style.strokePattern != nil {
sp = sp.Copy()
sp = sp.Stroke(style.lineWidth, style.lineCap, style.lineJoin, canvas.Tolerance)
sp = applyClipPath(sp, strokeClip)
if style.strokePattern != nil {
r.renderPattern(ctx, style.strokePattern, pageH, sp, patternCTM)
} else {
ctx.SetFill(strokePaint)
ctx.SetStrokeColor(canvas.Transparent)
drawShdPath(ctx, sp, strokeView)
}
} else {
ctx.DrawPath(0, 0, sp)
}
@@ -274,22 +282,25 @@ func (r *Renderer) renderPath(ctx *canvas.Context, obj PathObject, pageH float64
}
// renderPattern 渲染图案填充
// 入参: ctx 画布上下文, pattern 图案对象, defaultColor 默认颜色, pageH 页面高度, clip 填充区域, parentCTM 父级CTM, bx 边界X坐标, by 边界Y坐标
func (r *Renderer) renderPattern(ctx *canvas.Context, pattern *Pattern, defaultColor color.Color, pageH float64, clip *canvas.Path, parentCTM Matrix, bx, by float64) {
if pattern == nil || clip == nil || len(pattern.CellContent.Objects) == 0 {
// 入参: ctx 画布上下文, pattern 底纹画刷, pageH 页面高度, clip 绘制区域, objectCTM 对象变换矩阵
func (r *Renderer) renderPattern(ctx *canvas.Context, pattern *patternPaint, pageH float64, clip *canvas.Path, objectCTM Matrix) {
if clip == nil || clip.Empty() || len(pattern.CellContent.Objects) == 0 {
return
}
xStep, yStep := pattern.XStep, pattern.YStep
if xStep == 0 {
if xStep < pattern.Width {
xStep = pattern.Width
}
if yStep == 0 {
if yStep < pattern.Height {
yStep = pattern.Height
}
if xStep <= 0 || yStep <= 0 {
return
}
patternCTM := TranslationMatrix(bx, by).Multiply(parentCTM).Multiply(NewMatrix(pattern.CTM))
patternCTM := NewMatrix(pattern.CTM)
if pattern.RelativeTo != "Page" {
patternCTM = objectCTM.Multiply(patternCTM)
}
invCTM, ok := patternCTM.Invert()
if !ok {
return
@@ -329,7 +340,8 @@ func (r *Renderer) renderPattern(ctx *canvas.Context, pattern *Pattern, defaultC
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, true, clip)
obj = mergeGraphicObjectAlpha(obj, pattern.alpha)
r.renderObject(ctx, obj, pageH, pattern.color, pattern.color, 0, &tileCTM, true, clip)
}
}
}
+41 -14
View File
@@ -76,15 +76,19 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
fillColor = colorWithAlpha(canvas.Black, obj.Alpha)
}
var fillPaint any = fillColor
var fillPattern *patternPaint
var fillColorNode *FillColor
if dp != nil && dp.FillColor != nil {
fillColorNode := withFillAlpha(dp.FillColor, obj.Alpha)
fillColor = parseFillColor(fillColorNode)
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
fillColorNode = dp.FillColor
}
if obj.FillColor != nil {
fillColorNode := withFillAlpha(obj.FillColor, obj.Alpha)
fillColor = parseFillColor(fillColorNode)
fillPaint = parseFillPaint(fillColorNode, bx, by, pageH)
fillColorNode = obj.FillColor
}
if fillColorNode != nil {
fillColorNode = withFillAlpha(fillColorNode, obj.Alpha)
fillColor = r.parseFillColor(fillColorNode)
fillPaint = r.parseFillPaint(fillColorNode, bx, by, pageH)
fillPattern = r.parsePatternPaint(fillColorNode.Pattern, fillColorNode.Value, fillColorNode.Index, fillColorNode.ColorSpace, fillColorNode.Alpha)
}
if fillPaint == nil {
fillPaint = fillColor
@@ -96,10 +100,10 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
strokeView := canvas.Identity
if shouldStroke {
if dp != nil {
strokeStyle.applyDrawParam(dp, bx, by, pageH, obj.Alpha)
strokeStyle.applyDrawParam(r, dp, bx, by, pageH, obj.Alpha)
}
if obj.StrokeColor != nil {
strokeStyle.applyStrokeColor(obj.StrokeColor, bx, by, pageH, obj.Alpha)
strokeStyle.applyStrokeColor(r, obj.StrokeColor, bx, by, pageH, obj.Alpha)
}
if obj.LineWidth > 0 {
strokeStyle.lineWidth = obj.LineWidth
@@ -111,6 +115,10 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
strokeStyle.strokePaint, shadingClip, strokeView = resolveShdPaint(ctx, strokeStyle.strokePaint)
strokeClip = intersectClipPath(strokeClip, shadingClip)
}
patternCTM := TranslationMatrix(bx, by).Multiply(ctm)
if boundaryInCTM && parentCTM != nil {
patternCTM = parentCTM.Multiply(TranslationMatrix(bx, by)).Multiply(localCTM)
}
fontStyle := canvas.FontRegular
weight := obj.Weight
if weight == 0 && dp != nil && dp.Weight > 0 {
@@ -173,7 +181,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
}
dxs, dys := parseFloats(tc.DeltaX), parseFloats(tc.DeltaY)
xs, ys := parseFloats(tc.X), parseFloats(tc.Y)
drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) || fillClip != nil || shadedFill || shouldStroke
drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) || fillClip != nil || shadedFill || fillPattern != nil || shouldStroke
cx, cy := 0.0, 0.0
previousAdvance := 0.0
if len(xs) > 0 {
@@ -238,7 +246,9 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
transform = transform.Mul(glyphMatrix)
}
path := glyphPath.Copy().Transform(transform.Scale(scaleX, 1))
if shouldFill && fillPaint != nil {
if shouldFill && fillPattern != nil {
r.renderPattern(ctx, fillPattern, pageH, applyClipPath(path.Copy(), fillClip), patternCTM)
} else if shouldFill && fillPaint != nil {
ctx.SetFill(fillPaint)
ctx.SetStrokeColor(canvas.Transparent)
drawShdPath(ctx, applyClipPath(path.Copy(), fillClip), fillView)
@@ -247,26 +257,35 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
path = path.Dash(strokeStyle.dashOffset, strokeStyle.dashPattern...)
}
path = path.Stroke(strokeStyle.lineWidth, strokeStyle.lineCap, strokeStyle.lineJoin, canvas.Tolerance)
path = applyClipPath(path, strokeClip)
if strokeStyle.strokePattern != nil {
r.renderPattern(ctx, strokeStyle.strokePattern, pageH, path, patternCTM)
} else {
ctx.SetFill(strokeStyle.strokePaint)
ctx.SetStrokeColor(canvas.Transparent)
drawShdPath(ctx, applyClipPath(path, strokeClip), strokeView)
drawShdPath(ctx, path, strokeView)
}
if hasUnderline {
underline := &canvas.Path{}
underline.MoveTo(0, -sizeMM*0.1)
underline.LineTo(glyphWidth*scaleX, -sizeMM*0.1)
underline = underline.Stroke(sizeMM*0.05, canvas.ButtCap, canvas.MiterJoin, canvas.Tolerance)
if shouldFill {
if shouldFill && fillPattern != nil {
r.renderPattern(ctx, fillPattern, pageH, applyClipPath(underline.Transform(transform), clipPath), patternCTM)
} else if shouldFill {
ctx.SetFillColor(fillColor)
ctx.DrawPath(0, 0, applyClipPath(underline.Transform(transform), clipPath))
} else if strokeStyle.strokePattern != nil {
r.renderPattern(ctx, strokeStyle.strokePattern, pageH, applyClipPath(underline.Transform(transform), strokeClip), patternCTM)
} else {
drawShdPath(ctx, applyClipPath(underline.Transform(transform), strokeClip), strokeView)
}
}
continue
}
if fillPaint != nil {
if fillPaint != nil || fillPattern != nil {
ctx.SetFill(fillPaint)
if fillClip != nil || shadedFill {
if fillClip != nil || shadedFill || fillPattern != nil {
scaleX := hScale
if advanceLimit > 0 && glyphWidth*scaleX > advanceLimit {
scaleX = advanceLimit / glyphWidth
@@ -277,7 +296,11 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
textTransform = textTransform.Mul(glyphMatrix)
}
glyphPath = applyClipPath(glyphPath.Copy().Transform(textTransform.Scale(scaleX, 1)), fillClip)
if fillPattern != nil {
r.renderPattern(ctx, fillPattern, pageH, glyphPath, patternCTM)
} else {
drawShdPath(ctx, glyphPath, fillView)
}
if hasUnderline {
uw := sizeMM * 0.05
off := sizeMM * 0.1
@@ -286,10 +309,14 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
underline.LineTo(textWidth, -off)
underline = underline.Stroke(uw, canvas.ButtCap, canvas.MiterJoin, canvas.Tolerance)
underline = applyClipPath(underline.Transform(textTransform), clipPath)
if fillPattern != nil {
r.renderPattern(ctx, fillPattern, pageH, underline, patternCTM)
} else {
ctx.SetFillColor(fillColor)
ctx.SetStrokeColor(canvas.Transparent)
ctx.DrawPath(0, 0, underline)
}
}
continue
}
drawGlyph := func(x, y float64) {
+14
View File
@@ -20,12 +20,26 @@ import "encoding/xml"
type Res struct {
XMLName xml.Name `xml:"Res"`
BaseLoc string `xml:"BaseLoc,attr"`
ColorSpaces ColorSpaces `xml:"ColorSpaces"`
Fonts Fonts `xml:"Fonts"`
MultiMedias MultiMedias `xml:"MultiMedias"`
DrawParams DrawParams `xml:"DrawParams"`
CompositeGraphicUnits CompositeGraphicUnits `xml:"CompositeGraphicUnits"`
}
// ColorSpaces 颜色空间集合
type ColorSpaces struct {
ColorSpace []ColorSpace `xml:"ColorSpace"`
}
// ColorSpace 颜色空间定义
type ColorSpace struct {
ID string `xml:"ID,attr"`
Type string `xml:"Type,attr"`
BitsPerComponent int `xml:"BitsPerComponent,attr"`
Palette []string `xml:"Palette>CV"`
}
// Fonts 字体集合
type Fonts struct {
Font []Font `xml:"Font"`