feat(文字渲染): 修复文字方向与字形旋转渲染
build.yaml / build (push) Successful in 38s

This commit is contained in:
xiaoqidun committed 2026-09-08 23:25:08 +08:00
1 parent a3fdf7e57d
commit 96a2bd7b85
2 files changed
+51 -30

No files matched your search

+24 -22
View File
@@ -77,28 +77,30 @@ type ClipArea struct {
// TextObject 文本对象 // TextObject 文本对象
type TextObject struct { type TextObject struct {
ID string `xml:"ID,attr"` ID string `xml:"ID,attr"`
Boundary string `xml:"Boundary,attr"` Boundary string `xml:"Boundary,attr"`
DrawParam string `xml:"DrawParam,attr"` DrawParam string `xml:"DrawParam,attr"`
LineWidth float64 `xml:"LineWidth,attr"` LineWidth float64 `xml:"LineWidth,attr"`
Font string `xml:"Font,attr"` Font string `xml:"Font,attr"`
Size float64 `xml:"Size,attr"` Size float64 `xml:"Size,attr"`
Weight int `xml:"Weight,attr"` Weight int `xml:"Weight,attr"`
Italic bool `xml:"Italic,attr"` Italic bool `xml:"Italic,attr"`
Decoration string `xml:"Decoration,attr"` Decoration string `xml:"Decoration,attr"`
HScale float64 `xml:"HScale,attr"` HScale float64 `xml:"HScale,attr"`
VScale float64 `xml:"VScale,attr"` VScale float64 `xml:"VScale,attr"`
CTM string `xml:"CTM,attr"` ReadDirection int `xml:"ReadDirection,attr"`
Alpha *int `xml:"Alpha,attr"` CharDirection int `xml:"CharDirection,attr"`
Visible *bool `xml:"Visible,attr"` CTM string `xml:"CTM,attr"`
Fill *bool `xml:"Fill,attr"` Alpha *int `xml:"Alpha,attr"`
Stroke *bool `xml:"Stroke,attr"` Visible *bool `xml:"Visible,attr"`
StrokeColor *StrokeColor `xml:"StrokeColor"` Fill *bool `xml:"Fill,attr"`
FillColor *FillColor `xml:"FillColor"` Stroke *bool `xml:"Stroke,attr"`
CGTransform []CGTransform `xml:"CGTransform"` StrokeColor *StrokeColor `xml:"StrokeColor"`
TextCode []TextCode `xml:"TextCode"` FillColor *FillColor `xml:"FillColor"`
Clips *Clips `xml:"Clips"` CGTransform []CGTransform `xml:"CGTransform"`
Actions []Action `xml:"Actions>Action"` TextCode []TextCode `xml:"TextCode"`
Clips *Clips `xml:"Clips"`
Actions []Action `xml:"Actions>Action"`
} }
// FillColor 填充颜色 // FillColor 填充颜色
+27 -8
View File
@@ -65,7 +65,8 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
if hScale == 0 { if hScale == 0 {
hScale = 1 hScale = 1
} }
useTextMatrix := hasTextMatrix(ctm) useTextMatrix := hasTextMatrix(ctm) || obj.CharDirection != 0
glyphMatrix := textMatrix(ctm).Rotate(-float64(obj.CharDirection))
if scale := ctm.YScale(); scale > 0 && !useTextMatrix { if scale := ctm.YScale(); scale > 0 && !useTextMatrix {
sizeMM *= scale sizeMM *= scale
} }
@@ -144,6 +145,16 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
glyphTransforms := r.textObjectGlyphTransforms(fontID, obj) glyphTransforms := r.textObjectGlyphTransforms(fontID, obj)
hasUnderline := strings.Contains(obj.Decoration, "Underline") hasUnderline := strings.Contains(obj.Decoration, "Underline")
useGlyphFillPaint := fillColorNode != nil && fillColorNode.AxialShd != nil useGlyphFillPaint := fillColorNode != nil && fillColorNode.AxialShd != nil
verticalAdvance := (obj.ReadDirection-obj.CharDirection)%180 != 0
advanceX, advanceY := 1.0, 0.0
switch obj.ReadDirection {
case 90:
advanceX, advanceY = 0, 1
case 180:
advanceX = -1
case 270:
advanceX, advanceY = 0, -1
}
codePos := 0 codePos := 0
for _, tc := range obj.TextCode { for _, tc := range obj.TextCode {
var runes []rune var runes []rune
@@ -159,7 +170,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
xs, ys := parseFloats(tc.X), parseFloats(tc.Y) xs, ys := parseFloats(tc.X), parseFloats(tc.Y)
drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) || clipPath != nil || shouldStroke drawAsPath := embeddedFont || textCodePositioned(tc, xs, ys) || clipPath != nil || shouldStroke
cx, cy := 0.0, 0.0 cx, cy := 0.0, 0.0
previousWidth := 0.0 previousAdvance := 0.0
if len(xs) > 0 { if len(xs) > 0 {
cx = xs[0] cx = xs[0]
} }
@@ -182,17 +193,22 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
if dx, ok := textDelta(dxs, i-1); ok { if dx, ok := textDelta(dxs, i-1); ok {
cx += dx cx += dx
} else if len(dys) == 0 { } else if len(dys) == 0 {
cx += previousWidth * hScale cx += previousAdvance * advanceX
} }
} }
previousWidth = glyphWidth
if i < len(ys) { if i < len(ys) {
cy = ys[i] cy = ys[i]
} else if i > 0 { } else if i > 0 {
if dy, ok := textDelta(dys, i-1); ok { if dy, ok := textDelta(dys, i-1); ok {
cy += dy cy += dy
} else if len(dxs) == 0 {
cy += previousAdvance * advanceY
} }
} }
previousAdvance = glyphWidth * hScale
if verticalAdvance {
previousAdvance = sizeMM
}
var canvasX, canvasY float64 var canvasX, canvasY float64
if boundaryInCTM && parentCTM != nil { if boundaryInCTM && parentCTM != nil {
tx, ty := localCTM.Transform(cx, cy) tx, ty := localCTM.Transform(cx, cy)
@@ -207,7 +223,10 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
if useGlyphFillPaint { if useGlyphFillPaint {
glyphFillPaint = parseFillPaint(fillColorNode, bx, by, pageH, canvasX, canvasY) glyphFillPaint = parseFillPaint(fillColorNode, bx, by, pageH, canvasX, canvasY)
} }
advanceLimit := textGlyphAdvanceLimit(dxs, dys, xs, i, len(glyphs), cx) advanceLimit := 0.0
if obj.CharDirection == 0 {
advanceLimit = textGlyphAdvanceLimit(dxs, dys, xs, i, len(glyphs), cx)
}
if shouldStroke { if shouldStroke {
scaleX := hScale scaleX := hScale
if advanceLimit > 0 && glyphWidth*scaleX > advanceLimit { if advanceLimit > 0 && glyphWidth*scaleX > advanceLimit {
@@ -215,7 +234,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
} }
transform := canvas.Identity.Translate(canvasX, canvasY) transform := canvas.Identity.Translate(canvasX, canvasY)
if useTextMatrix { if useTextMatrix {
transform = transform.Mul(textMatrix(ctm)) transform = transform.Mul(glyphMatrix)
} }
path := glyphPath.Copy().Transform(transform.Scale(scaleX, 1)) path := glyphPath.Copy().Transform(transform.Scale(scaleX, 1))
if shouldFill && glyphFillPaint != nil { if shouldFill && glyphFillPaint != nil {
@@ -252,7 +271,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
textWidth = glyphWidth * scaleX textWidth = glyphWidth * scaleX
textTransform := canvas.Identity.Translate(canvasX, canvasY) textTransform := canvas.Identity.Translate(canvasX, canvasY)
if useTextMatrix { if useTextMatrix {
textTransform = textTransform.Mul(textMatrix(ctm)) textTransform = textTransform.Mul(glyphMatrix)
} }
glyphPath = applyClipPath(glyphPath.Copy().Transform(textTransform.Scale(scaleX, 1)), clipPath) glyphPath = applyClipPath(glyphPath.Copy().Transform(textTransform.Scale(scaleX, 1)), clipPath)
ctx.DrawPath(0, 0, glyphPath) ctx.DrawPath(0, 0, glyphPath)
@@ -308,7 +327,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
if useTextMatrix { if useTextMatrix {
ctx.Push() ctx.Push()
ctx.Translate(canvasX, canvasY) ctx.Translate(canvasX, canvasY)
ctx.ComposeView(textMatrix(ctm)) ctx.ComposeView(glyphMatrix)
drawGlyph(0, 0) drawGlyph(0, 0)
if hasUnderline { if hasUnderline {
uw := sizeMM * 0.05 uw := sizeMM * 0.05