mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:31:12 +08:00
1 parent
8f9e194c0c
commit
bb3abdea1d
22 files changed
+322
-122
No files matched your search
+32
-20
@@ -1036,6 +1036,9 @@ func getCFFCharsetInfo(data []byte, numGlyphs int) ([]int, string, string, int,
|
||||
if topDictData == nil {
|
||||
return nil, "", "", 0, false
|
||||
}
|
||||
if _, _, _, err := scanCFFIndex(data, stringIndexOff); err != nil {
|
||||
return nil, "", "", 0, false
|
||||
}
|
||||
td := parseCFFDict(topDictData)
|
||||
registry, ordering := getCFFROS(data, stringIndexOff, td)
|
||||
actual, err := parseCFFAndCountGlyphs(data)
|
||||
@@ -1067,7 +1070,18 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
mapping := make(map[rune]uint16)
|
||||
return cffCharsetMapping(data, sids, registry, ordering, stringIndexOff)
|
||||
}
|
||||
|
||||
// cffCharsetMapping 根据已解析的CFF字符集恢复Unicode映射
|
||||
// 入参: data CFF数据, sids 字符标识, registry 注册机构, ordering 字符集, stringIndexOff 字符串索引偏移
|
||||
// 返回: map[rune]uint16 恢复的映射表
|
||||
func cffCharsetMapping(data []byte, sids []int, registry, ordering string, stringIndexOff int) map[rune]uint16 {
|
||||
capacity := max(0, len(sids)-1)
|
||||
if registry == "" || registry == "Adobe" && ordering == "GB1" {
|
||||
capacity *= 2
|
||||
}
|
||||
mapping := make(map[rune]uint16, capacity)
|
||||
if registry == "Adobe" && ordering == "GB1" {
|
||||
for gid, cid := range sids {
|
||||
if gid == 0 {
|
||||
@@ -1100,7 +1114,7 @@ func getCmapFromCFF(data []byte, numGlyphs int) map[rune]uint16 {
|
||||
}
|
||||
} else {
|
||||
idx := sid - 391
|
||||
name = readStringIndexItem(data, stringIndexOff, idx)
|
||||
name = string(readStringIndexItem(data, stringIndexOff, idx))
|
||||
}
|
||||
r := rune(0)
|
||||
if name != "" {
|
||||
@@ -1127,11 +1141,11 @@ func GetCFFCIDRuneMap(data []byte) map[uint16]rune {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sids, registry, _, _, ok := getCFFCharsetInfo(cffData, numGlyphs)
|
||||
sids, registry, ordering, stringIndexOff, ok := getCFFCharsetInfo(cffData, numGlyphs)
|
||||
if !ok || registry == "" {
|
||||
return nil
|
||||
}
|
||||
mapping := getCmapFromCFF(cffData, numGlyphs)
|
||||
mapping := cffCharsetMapping(cffData, sids, registry, ordering, stringIndexOff)
|
||||
if len(mapping) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1187,7 +1201,7 @@ func getCFFData(data []byte) []byte {
|
||||
}
|
||||
offset := int(binary.BigEndian.Uint32(data[pos+8 : pos+12]))
|
||||
length := int(binary.BigEndian.Uint32(data[pos+12 : pos+16]))
|
||||
if offset < 0 || length < 0 || offset+length > len(data) {
|
||||
if offset < 0 || length < 0 || offset > len(data) || length > len(data)-offset {
|
||||
return nil
|
||||
}
|
||||
return data[offset : offset+length]
|
||||
@@ -1269,31 +1283,29 @@ func parseCFFCharset(data []byte, offset, count int) ([]int, error) {
|
||||
|
||||
// readStringIndexItem 读取CFF字符串索引项
|
||||
// 入参: data CFF数据, offset 索引偏移, idx 索引号
|
||||
// 返回: string 读取的字符串
|
||||
func readStringIndexItem(data []byte, offset int, idx int) string {
|
||||
if offset >= len(data) {
|
||||
return ""
|
||||
// 返回: []byte 共享原始数据的只读字符串,索引无效时为nil
|
||||
func readStringIndexItem(data []byte, offset int, idx int) []byte {
|
||||
if offset < 0 || offset > len(data)-2 || idx < 0 {
|
||||
return nil
|
||||
}
|
||||
count := int(binary.BigEndian.Uint16(data[offset:]))
|
||||
if idx >= count || offset > len(data)-3 {
|
||||
return nil
|
||||
}
|
||||
offSize := int(data[offset+2])
|
||||
if idx >= count {
|
||||
return ""
|
||||
if offSize < 1 || offSize > 4 || count+1 > (len(data)-offset-3)/offSize {
|
||||
return nil
|
||||
}
|
||||
offArrayStart := offset + 3
|
||||
p1 := offArrayStart + idx*offSize
|
||||
p2 := p1 + offSize
|
||||
if p2+offSize > len(data) {
|
||||
return ""
|
||||
}
|
||||
loc1 := readCFFOffset(data, p1, offSize)
|
||||
loc2 := readCFFOffset(data, p2, offSize)
|
||||
dataStart := offArrayStart + (count+1)*offSize
|
||||
start := dataStart + loc1 - 1
|
||||
length := loc2 - loc1
|
||||
if start < 0 || start+length > len(data) {
|
||||
return ""
|
||||
if loc1 < 1 || loc2 < loc1 || loc2-1 > len(data)-dataStart {
|
||||
return nil
|
||||
}
|
||||
return string(data[start : start+length])
|
||||
return data[dataStart+loc1-1 : dataStart+loc2-1]
|
||||
}
|
||||
|
||||
// getCFFROS 读取CID字体ROS信息
|
||||
@@ -1317,7 +1329,7 @@ func getCFFSIDString(data []byte, stringIndexOff int, sid int) string {
|
||||
return cffStandardStrings[sid]
|
||||
}
|
||||
if sid > 390 {
|
||||
return readStringIndexItem(data, stringIndexOff, sid-391)
|
||||
return string(readStringIndexItem(data, stringIndexOff, sid-391))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ func centerArc(start Point, arc Segment) (geometryArc, error) {
|
||||
}
|
||||
angle := math.Mod(arc.Rotation, 360) * math.Pi / 180
|
||||
sine, cosine := math.Sincos(angle)
|
||||
dx, dy := (start.X-arc.End.X)/2, (start.Y-arc.End.Y)/2
|
||||
dx, dy := arcCoordinateMean(start.X, -arc.End.X), arcCoordinateMean(start.Y, -arc.End.Y)
|
||||
x, y := cosine*dx+sine*dy, -sine*dx+cosine*dy
|
||||
scale := math.Hypot(x/rx, y/ry)
|
||||
if scale > 1 {
|
||||
@@ -139,13 +139,24 @@ func centerArc(start Point, arc Segment) (geometryArc, error) {
|
||||
if !arc.Sweep && delta > 0 {
|
||||
delta -= 2 * math.Pi
|
||||
}
|
||||
center := Point{cosine*cx - sine*cy + (start.X+arc.End.X)/2, sine*cx + cosine*cy + (start.Y+arc.End.Y)/2}
|
||||
center := Point{cosine*cx - sine*cy + arcCoordinateMean(start.X, arc.End.X), sine*cx + cosine*cy + arcCoordinateMean(start.Y, arc.End.Y)}
|
||||
if !finite(center.X) || !finite(center.Y) || !finite(rx) || !finite(ry) || !finite(delta) || !finite(theta) || delta == 0 {
|
||||
return geometryArc{}, fmt.Errorf("invalid arc extent")
|
||||
}
|
||||
return geometryArc{center, Point{rx * cosine, rx * sine}, Point{-ry * sine, ry * cosine}, theta, delta}, nil
|
||||
}
|
||||
|
||||
// arcCoordinateMean 计算有限坐标均值,仅在求和溢出时先减半,避免微小坐标提前下溢
|
||||
// 入参: a 第一个坐标, b 第二个坐标
|
||||
// 返回: float64 坐标均值
|
||||
func arcCoordinateMean(a, b float64) float64 {
|
||||
value := (a + b) / 2
|
||||
if math.IsInf(value, 0) {
|
||||
return a/2 + b/2
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// point 计算椭圆参数对应的坐标
|
||||
// 入参: angle 参数角
|
||||
// 返回: Point 椭圆上的点
|
||||
|
||||
@@ -125,7 +125,7 @@ func (m Matrix) Invert() (Matrix, bool) {
|
||||
return n, true
|
||||
}
|
||||
|
||||
// matrixProductDifference 计算两个乘积之差,检查溢出、下溢及相消造成的精度损失
|
||||
// matrixProductDifference 补偿乘积相减的舍入残差,检查溢出、下溢及严重相消
|
||||
// 入参: a 第一个乘数, b 第二个乘数, c 第三个乘数, d 第四个乘数
|
||||
// 返回: float64 乘积之差, bool 是否可直接用于求逆
|
||||
func matrixProductDifference(a, b, c, d float64) (float64, bool) {
|
||||
@@ -134,6 +134,9 @@ func matrixProductDifference(a, b, c, d float64) (float64, bool) {
|
||||
if !finite(value) || x == 0 && a != 0 && b != 0 || y == 0 && c != 0 && d != 0 {
|
||||
return value, false
|
||||
}
|
||||
if x != 0 && y != 0 {
|
||||
value = math.FMA(a, b, -y) + math.FMA(-c, d, y)
|
||||
}
|
||||
return value, x == 0 && y == 0 || math.Abs(value) >= 0x1p-1022 && math.Abs(value) >= 0x1p-48*math.Max(math.Abs(x), math.Abs(y))
|
||||
}
|
||||
|
||||
@@ -169,7 +172,7 @@ func TranslationMatrix(x, y float64) Matrix {
|
||||
// YScale 获取Y轴缩放比例
|
||||
// 返回: float64 缩放比例
|
||||
func (m Matrix) YScale() float64 {
|
||||
return math.Sqrt(m.c*m.c + m.d*m.d)
|
||||
return math.Hypot(m.c, m.d)
|
||||
}
|
||||
|
||||
// Equal 比较毫米坐标,使用固定绝对误差,不受绘图库全局精度影响
|
||||
|
||||
@@ -33,10 +33,7 @@ func (p Path) Transform(matrix Matrix) (Path, error) {
|
||||
}
|
||||
}
|
||||
matrixScale := math.Max(math.Max(math.Abs(matrix.a), math.Abs(matrix.b)), math.Max(math.Abs(matrix.c), math.Abs(matrix.d)))
|
||||
determinant := 0.0
|
||||
if matrixScale != 0 {
|
||||
determinant = (matrix.a/matrixScale)*(matrix.d/matrixScale) - (matrix.b/matrixScale)*(matrix.c/matrixScale)
|
||||
}
|
||||
determinant, _ := matrixProductSum(matrix.a, matrix.d, -matrix.b, matrix.c)
|
||||
transform := func(p Point) Point {
|
||||
x, y := matrix.Transform(p.X, p.Y)
|
||||
return Point{x, y}
|
||||
|
||||
@@ -451,13 +451,11 @@ func GrayAt(img image.Image, x, y int) color.Gray {
|
||||
case *colorImage:
|
||||
return GrayAt(src.source, x, y)
|
||||
case *maskedTIFFImage:
|
||||
if src.NRGBA64At(x, y).A == 0 {
|
||||
if src.alphaAt(x, y) == 0 {
|
||||
return color.Gray{}
|
||||
}
|
||||
bounds, size := src.source.Bounds(), src.bounds.Size()
|
||||
sx := bounds.Min.X + int((uint64(x-src.bounds.Min.X)*uint64(bounds.Dx())+uint64(bounds.Dx()/2))/uint64(size.X))
|
||||
sy := bounds.Min.Y + int((uint64(y-src.bounds.Min.Y)*uint64(bounds.Dy())+uint64(bounds.Dy()/2))/uint64(size.Y))
|
||||
return GrayAt(src.source, sx, sy)
|
||||
point := src.samplePoint(src.source.Bounds(), x, y)
|
||||
return GrayAt(src.source, point.X, point.Y)
|
||||
case *transparentEdgeImage:
|
||||
if x <= src.rect.Min.X || y <= src.rect.Min.Y || x >= src.rect.Max.X-1 || y >= src.rect.Max.Y-1 {
|
||||
return color.Gray{}
|
||||
@@ -534,6 +532,39 @@ func NRGBAAt(img image.Image, x, y int) color.NRGBA {
|
||||
// 返回: uint32 十六位透明度
|
||||
func AlphaAt(img image.Image, x, y int) uint32 {
|
||||
switch source := img.(type) {
|
||||
case *maskedTIFFImage:
|
||||
return source.alphaAt(x, y)
|
||||
case *image.YCbCr, *image.CMYK, *image.Gray, *image.Gray16:
|
||||
return 65535
|
||||
case *image.NYCbCrA:
|
||||
if !image.Pt(x, y).In(source.Rect) {
|
||||
return 0
|
||||
}
|
||||
return uint32(source.A[source.AOffset(x, y)]) * 257
|
||||
case *orientedTIFFImage:
|
||||
point, ok := source.sourcePoint(x, y)
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
return AlphaAt(source.source, point.X, point.Y)
|
||||
case *planarTIFFImage:
|
||||
if !image.Pt(x, y).In(source.bounds) {
|
||||
return 0
|
||||
}
|
||||
if source.alpha != 0 {
|
||||
index := 3
|
||||
if source.cmyk {
|
||||
index = 4
|
||||
} else if source.gray || len(source.palette) != 0 {
|
||||
index = 1
|
||||
}
|
||||
return uint32(source.sampleAt(index, x, y))
|
||||
}
|
||||
if len(source.palette) != 0 {
|
||||
index := uint32(source.sampleAt(0, x, y)) * uint32(len(source.palette)-1) / 65535
|
||||
return uint32(source.palette[index].A)
|
||||
}
|
||||
return 65535
|
||||
case *image.RGBA:
|
||||
if !(image.Point{X: x, Y: y}).In(source.Rect) {
|
||||
return 0
|
||||
|
||||
@@ -49,6 +49,36 @@ func (s *maskedTIFFImage) Bounds() image.Rectangle { return s.bounds }
|
||||
// 返回: color.Model 颜色模型
|
||||
func (s *maskedTIFFImage) ColorModel() color.Model { return color.NRGBA64Model }
|
||||
|
||||
// samplePoint 将显示坐标映射到主图或蒙版的像素中心
|
||||
// 入参: bounds 采样图像范围, x 横坐标, y 纵坐标
|
||||
// 返回: image.Point 采样坐标
|
||||
func (s *maskedTIFFImage) samplePoint(bounds image.Rectangle, x, y int) image.Point {
|
||||
size := s.bounds.Size()
|
||||
x, y = x-s.bounds.Min.X, y-s.bounds.Min.Y
|
||||
if bounds.Size() == size {
|
||||
return bounds.Min.Add(image.Pt(x, y))
|
||||
}
|
||||
return image.Pt(
|
||||
bounds.Min.X+int((uint64(x)*uint64(bounds.Dx())+uint64(bounds.Dx()/2))/uint64(size.X)),
|
||||
bounds.Min.Y+int((uint64(y)*uint64(bounds.Dy())+uint64(bounds.Dy()/2))/uint64(size.Y)),
|
||||
)
|
||||
}
|
||||
|
||||
// alphaAt 读取蒙版与主图透明度,不解算主图颜色
|
||||
// 入参: x 横坐标, y 纵坐标
|
||||
// 返回: uint32 十六位透明度
|
||||
func (s *maskedTIFFImage) alphaAt(x, y int) uint32 {
|
||||
if !image.Pt(x, y).In(s.bounds) {
|
||||
return 0
|
||||
}
|
||||
mask := s.samplePoint(s.mask.Bounds(), x, y)
|
||||
if NRGBA64At(s.mask, mask.X, mask.Y).R == 0 {
|
||||
return 0
|
||||
}
|
||||
source := s.samplePoint(s.source.Bounds(), x, y)
|
||||
return AlphaAt(s.source, source.X, source.Y)
|
||||
}
|
||||
|
||||
// NRGBA64At 按像素中心读取主图及蒙版,零位隐藏原图并保留隐藏颜色
|
||||
// 入参: x 横坐标, y 纵坐标
|
||||
// 返回: color.NRGBA64 非预乘十六位颜色
|
||||
@@ -56,16 +86,10 @@ func (s *maskedTIFFImage) NRGBA64At(x, y int) color.NRGBA64 {
|
||||
if !image.Pt(x, y).In(s.bounds) {
|
||||
return color.NRGBA64{}
|
||||
}
|
||||
size := s.bounds.Size()
|
||||
x, y = x-s.bounds.Min.X, y-s.bounds.Min.Y
|
||||
b := s.source.Bounds()
|
||||
sx := b.Min.X + int((uint64(x)*uint64(b.Dx())+uint64(b.Dx()/2))/uint64(size.X))
|
||||
sy := b.Min.Y + int((uint64(y)*uint64(b.Dy())+uint64(b.Dy()/2))/uint64(size.Y))
|
||||
pixel := NRGBA64At(s.source, sx, sy)
|
||||
b = s.mask.Bounds()
|
||||
mx := b.Min.X + int((uint64(x)*uint64(b.Dx())+uint64(b.Dx()/2))/uint64(size.X))
|
||||
my := b.Min.Y + int((uint64(y)*uint64(b.Dy())+uint64(b.Dy()/2))/uint64(size.Y))
|
||||
if NRGBA64At(s.mask, mx, my).R == 0 {
|
||||
source := s.samplePoint(s.source.Bounds(), x, y)
|
||||
pixel := NRGBA64At(s.source, source.X, source.Y)
|
||||
mask := s.samplePoint(s.mask.Bounds(), x, y)
|
||||
if NRGBA64At(s.mask, mask.X, mask.Y).R == 0 {
|
||||
pixel.A = 0
|
||||
}
|
||||
return pixel
|
||||
|
||||
Reference in new issue
Block a user