feat(转换能力): 完善转换渲染,优化字体加载
build.yaml / build (push) Successful in 40s

This commit is contained in:
xiaoqidun committed 2026-09-27 08:03:10 +08:00
1 parent 983a856736
commit 98d9effc8c
18 files changed
+672 -137

No files matched your search

+67 -48
View File
@@ -16,72 +16,61 @@ package ofdgo
import (
"math"
"slices"
"github.com/xiaoqidun/pdfgo"
)
// pdfGradientSegments 将线性源颜色及设备四色的截断点表达为OFD分段
// gradientSegments 保留线性源颜色的设备分量,校准颜色经源空间转换
// 入参: stops 源颜色分段, space 源颜色空间
// 返回: []ShdSegment 等价RGB分段, error 不可线性表达的变换
func pdfGradientSegments(stops []pdfgo.GradientStop, space *pdfgo.ColorSpace) ([]ShdSegment, error) {
if len(stops) == 0 {
return nil, &pdfgo.UnsupportedError{Feature: "nonlinear gradient conversion"}
// 返回: []ShdSegment OFD颜色分段, error 不可线性表达的变换
func (p *pdfImporter) gradientSegments(stops []pdfgo.GradientStop, space *pdfgo.ColorSpace) ([]ShdSegment, error) {
if err := pdfGradientStopsError(stops, space); err != nil {
return nil, err
}
calibrated := space != nil && space.Calibrated() && !space.SRGBEquivalent()
cmyk := space != nil && space.Model == "DeviceCMYK" && !space.Calibrated()
segments := make([]ShdSegment, 0, len(stops))
appendStop := func(position float64, rgb [3]float64) {
segments = append(segments, ShdSegment{Position: position, Color: ShdColor{Value: pdfNumbers(math.Round(rgb[0]*255), math.Round(rgb[1]*255), math.Round(rgb[2]*255))}})
}
for i, b := range stops {
if i > 0 {
a := stops[i-1]
if a.Position != b.Position && a.Values != b.Values {
if calibrated {
return nil, &pdfgo.UnsupportedError{Feature: "nonlinear ICC gradient conversion"}
}
if cmyk {
var crossings []float64
for c := 0; c < 3; c++ {
start, end := a.Values[c]+a.Values[3], b.Values[c]+b.Values[3]
if start != end {
if t := (1 - start) / (end - start); t > 0 && t < 1 {
crossings = append(crossings, t)
}
}
}
slices.Sort(crossings)
for _, t := range slices.Compact(crossings) {
var values [4]float64
for c := range values {
values[c] = a.Values[c]*(1-t) + b.Values[c]*t
}
rgb, err := space.RGB(values[:], "RelativeColorimetric")
if err != nil {
return nil, err
}
appendStop(a.Position+(b.Position-a.Position)*t, rgb)
}
}
}
segments := make([]ShdSegment, len(stops))
for i, stop := range stops {
paint := pdfgo.Paint{RGB: stop.RGB, Alpha: 1}
if cmyk {
paint.CMYK = &stop.Values
}
appendStop(b.Position, b.RGB)
color := p.color(paint)
segments[i] = ShdSegment{Position: stop.Position, Color: ShdColor{Value: color.Value, ColorSpace: color.ColorSpace}}
}
return segments, nil
}
// pdfGradientStopsError 检查源颜色分段是否可由OFD基本颜色表达
// 入参: stops 源颜色分段, space 源颜色空间
// 返回: error 不可线性表达的变换
func pdfGradientStopsError(stops []pdfgo.GradientStop, space *pdfgo.ColorSpace) error {
if len(stops) == 0 {
return &pdfgo.UnsupportedError{Feature: "nonlinear gradient conversion"}
}
calibrated := space != nil && space.Calibrated() && !space.SRGBEquivalent()
if calibrated {
for i := 1; i < len(stops); i++ {
a, b := stops[i-1], stops[i]
if a.Position != b.Position && a.Values != b.Values {
return &pdfgo.UnsupportedError{Feature: "nonlinear ICC gradient conversion"}
}
}
}
return nil
}
// pdfGradientError 检查渐变能否精确映射为OFD线性分段
// 入参: paint 画刷
// 返回: error 不可等价表达的渐变
func pdfGradientError(paint pdfgo.Paint) error {
if paint.Mesh != nil {
return &pdfgo.UnsupportedError{Feature: "bicubic mesh conversion"}
}
if paint.Axial != nil {
_, err := pdfGradientSegments(paint.Axial.Stops, paint.Axial.Space)
return err
return pdfGradientStopsError(paint.Axial.Stops, paint.Axial.Space)
}
if paint.Radial != nil {
_, err := pdfGradientSegments(paint.Radial.Stops, paint.Radial.Space)
return err
return pdfGradientStopsError(paint.Radial.Stops, paint.Radial.Space)
}
return nil
}
@@ -90,7 +79,30 @@ func pdfGradientError(paint pdfgo.Paint) error {
// 入参: mark 路径绘制信息
// 返回: error 几何或着色错误
func (p *pdfImporter) gradientPath(mark pdfgo.PathMark) error {
return p.compositeRegion(nil, pdfCompositeNode{path: &mark}, &pdfgo.ColorSpace{Model: "DeviceRGB"}, false)
cmyk := true
for i, paint := range []pdfgo.Paint{mark.Style.Fill, mark.Style.Stroke} {
if i == 0 && !mark.Fill || i == 1 && !mark.Stroke {
continue
}
space := paint.Space
switch {
case paint.Axial != nil:
space = paint.Axial.Space
case paint.Radial != nil:
space = paint.Radial.Space
case paint.Mesh != nil:
space = paint.Mesh.Space
}
if paint.CMYK == nil && (space == nil || space.Model != "DeviceCMYK" || space.Calibrated()) {
cmyk = false
break
}
}
space := &pdfgo.ColorSpace{Model: "DeviceRGB"}
if cmyk {
space.Model = "DeviceCMYK"
}
return p.compositeRegion(nil, pdfCompositeNode{path: &mark}, space, false)
}
// pdfShadingPosition 计算渐变参数,未延伸区域及不存在非负圆半径的位置不着色
@@ -103,6 +115,13 @@ func pdfShadingPosition(paint pdfgo.Paint, point pdfgo.Point) (float64, bool) {
return t, finite(t) && (t >= 0 || g.Extend[0]) && (t <= 1 || g.Extend[1])
}
g := paint.Radial
if g.Matrix != (pdfgo.Matrix{}) {
inverse, ok := g.Matrix.Inverse()
if !ok {
return 0, false
}
point = inverse.Apply(point)
}
dx, dy, dr := g.End.X-g.Start.X, g.End.Y-g.Start.Y, g.EndRadius-g.StartRadius
px, py := point.X-g.Start.X, point.Y-g.Start.Y
a, b, c := dx*dx+dy*dy-dr*dr, -2*(px*dx+py*dy+g.StartRadius*dr), px*px+py*py-g.StartRadius*g.StartRadius