mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 08:46:55 +08:00
This commit is contained in:
1 parent
f44d0d8a60
commit
580e7043c4
31 files changed
+1488
-204
No files matched your search
@@ -16,6 +16,7 @@ package ofdgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -23,6 +24,96 @@ import (
|
||||
// 坐标以左上角为原点,单位为毫米,子路径从Move开始,Close回到当前子路径起点
|
||||
type GeometryPath []GeometrySegment
|
||||
|
||||
// ParseGeometryPath 解析标准OFD紧缩路径,保留弧线和原始数值精度
|
||||
// 入参: data 紧缩路径
|
||||
// 返回: GeometryPath 独立路径, error 指令或参数错误
|
||||
func ParseGeometryPath(data string) (GeometryPath, error) {
|
||||
tokens := strings.Fields(data)
|
||||
if len(tokens) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if tokens[0] != "M" && tokens[0] != "S" {
|
||||
return nil, fmt.Errorf("path must start with M or S")
|
||||
}
|
||||
var result GeometryPath
|
||||
for i := 0; i < len(tokens); {
|
||||
command := tokens[i]
|
||||
i++
|
||||
count := 0
|
||||
switch command {
|
||||
case "M", "S", "L":
|
||||
count = 2
|
||||
case "Q":
|
||||
count = 4
|
||||
case "B":
|
||||
count = 6
|
||||
case "A":
|
||||
count = 7
|
||||
case "C":
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid path command %q", command)
|
||||
}
|
||||
if len(tokens)-i < count {
|
||||
return nil, fmt.Errorf("missing arguments for path command %s", command)
|
||||
}
|
||||
var values [7]float64
|
||||
for j, token := range tokens[i : i+count] {
|
||||
if command == "A" && (j == 3 || j == 4) && (token == "true" || token == "false") {
|
||||
if token == "true" {
|
||||
values[j] = 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
value, err := strconv.ParseFloat(token, 64)
|
||||
if err != nil || !finite(value) || strings.ContainsAny(token, "xX_") {
|
||||
return nil, fmt.Errorf("invalid number %q", token)
|
||||
}
|
||||
values[j] = value
|
||||
}
|
||||
i += count
|
||||
segment := GeometrySegment{}
|
||||
switch command {
|
||||
case "M", "S":
|
||||
segment.Verb, segment.End = GeometryMove, Point{values[0], values[1]}
|
||||
case "L":
|
||||
segment.Verb, segment.End = GeometryLine, Point{values[0], values[1]}
|
||||
case "Q":
|
||||
segment.Verb, segment.Control1, segment.End = GeometryQuad, Point{values[0], values[1]}, Point{values[2], values[3]}
|
||||
case "B":
|
||||
segment.Verb, segment.Control1, segment.Control2, segment.End = GeometryCubic, Point{values[0], values[1]}, Point{values[2], values[3]}, Point{values[4], values[5]}
|
||||
case "A":
|
||||
if values[0] < 0 || values[1] < 0 || values[3] != 0 && values[3] != 1 || values[4] != 0 && values[4] != 1 {
|
||||
return nil, fmt.Errorf("invalid arc radii or flags")
|
||||
}
|
||||
segment = GeometrySegment{Verb: GeometryArc, RadiusX: values[0], RadiusY: values[1], Rotation: values[2], Large: values[3] != 0, Sweep: values[4] != 0, End: Point{values[5], values[6]}}
|
||||
case "C":
|
||||
segment.Verb = GeometryClose
|
||||
}
|
||||
result = append(result, segment)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// objectGeometryPath 解析对象路径并由当前几何实现应用页面变换
|
||||
// 入参: geometry 几何后端, object 路径对象
|
||||
// 返回: GeometryPath 页面路径, error 参数或变换错误
|
||||
func objectGeometryPath(geometry GeometryBackend, object PathObject) (GeometryPath, error) {
|
||||
path, err := ParseGeometryPath(object.AbbreviatedData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
box, err := creationBox(object.Boundary)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if object.CTM != "" {
|
||||
if _, err := creationNumbers(object.CTM, 6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return geometry.Transform(path, TranslationMatrix(box.X, box.Y).Multiply(NewMatrix(object.CTM)))
|
||||
}
|
||||
|
||||
// closedGeometry 为填充轮廓补齐隐式闭合边,不改变源路径
|
||||
// 入参: path 填充路径
|
||||
// 返回: GeometryPath 显式闭合路径
|
||||
|
||||
Reference in new issue
Block a user