mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 13:56:32 +08:00
1 parent
b5a52dafb8
commit
6d9f1fbe41
4 files changed
+31
-117
No files matched your search
+27
-89
@@ -378,22 +378,6 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
delete(topDict, 1234)
|
||||
delete(topDict, 15)
|
||||
delete(topDict, 16)
|
||||
topDict[18] = []float64{float64(privSize), 0}
|
||||
var newCFF bytes.Buffer
|
||||
newCFF.Write(data[:hdrSize])
|
||||
newCFF.Write(nameIndexData)
|
||||
dummyDict := make(map[int][]float64)
|
||||
for k, v := range topDict {
|
||||
dummyDict[k] = v
|
||||
}
|
||||
dummyDict[17] = []float64{0}
|
||||
dummyDict[18] = []float64{float64(privSize), 0}
|
||||
dummyTopData := encodeCFFDict(dummyDict)
|
||||
topIdxSize := 2 + 1 + 8 + len(dummyTopData)
|
||||
dataStart := hdrSize + len(nameIndexData) + topIdxSize + len(stringIndexData) + len(globalSubrIndexData)
|
||||
charStringsPos := dataStart
|
||||
privatePos := charStringsPos + len(charStringsData)
|
||||
privateLen := privSize
|
||||
var finalPrivData []byte
|
||||
if len(privDictData) > 0 {
|
||||
pDict := parseCFFDict(privDictData)
|
||||
@@ -411,41 +395,8 @@ func sanitizeCFF(data []byte) ([]byte, error) {
|
||||
}
|
||||
}
|
||||
finalPrivData = encodeCFFDict(pDict)
|
||||
privateLen = len(finalPrivData)
|
||||
}
|
||||
localSubrsPos := privatePos + privateLen
|
||||
topDict[17] = []float64{float64(charStringsPos)}
|
||||
topDict[18] = []float64{float64(privateLen), float64(privatePos)}
|
||||
finalTopData := encodeCFFDict(topDict)
|
||||
topIndex := encodeCFFIndex([]([]byte){finalTopData})
|
||||
newCFF.Reset()
|
||||
newCFF.Write(data[:hdrSize])
|
||||
newCFF.Write(nameIndexData)
|
||||
newCFF.Write(topIndex)
|
||||
newCFF.Write(stringIndexData)
|
||||
newCFF.Write(globalSubrIndexData)
|
||||
if newCFF.Len() != dataStart {
|
||||
diff := newCFF.Len() - dataStart
|
||||
charStringsPos += diff
|
||||
privatePos += diff
|
||||
localSubrsPos += diff
|
||||
topDict[17] = []float64{float64(charStringsPos)}
|
||||
topDict[18] = []float64{float64(privateLen), float64(privatePos)}
|
||||
finalTopData = encodeCFFDict(topDict)
|
||||
topIndex = encodeCFFIndex([]([]byte){finalTopData})
|
||||
newCFF.Reset()
|
||||
newCFF.Write(data[:hdrSize])
|
||||
newCFF.Write(nameIndexData)
|
||||
newCFF.Write(topIndex)
|
||||
newCFF.Write(stringIndexData)
|
||||
newCFF.Write(globalSubrIndexData)
|
||||
}
|
||||
newCFF.Write(charStringsData)
|
||||
newCFF.Write(finalPrivData)
|
||||
if len(localSubrData) > 0 {
|
||||
newCFF.Write(localSubrData)
|
||||
}
|
||||
return newCFF.Bytes(), nil
|
||||
return encodeSanitizedCFF(data[:hdrSize], nameIndexData, topDict, stringIndexData, globalSubrIndexData, charStringsData, finalPrivData, localSubrData), nil
|
||||
}
|
||||
|
||||
// normalizeCFFPrivate 移除旧版CFF中不生效的缺省字段,拒绝需要额外解释的取值
|
||||
@@ -508,48 +459,35 @@ func sanitizeMultiFDCFF(data []byte, hdrSize int, nameIndexData []byte, topDict
|
||||
return nil, err
|
||||
}
|
||||
finalPrivData := encodeCFFDict(privateDict)
|
||||
privateLen := len(finalPrivData)
|
||||
charStringsData := encodeCFFIndex(inlined)
|
||||
topDict[18] = []float64{float64(privateLen), 0}
|
||||
var newCFF bytes.Buffer
|
||||
dummyDict := make(cffDict)
|
||||
for k, v := range topDict {
|
||||
dummyDict[k] = v
|
||||
return encodeSanitizedCFF(data[:hdrSize], nameIndexData, topDict, stringIndexData, globalSubrIndexData, charStringsData, finalPrivData, nil), nil
|
||||
}
|
||||
dummyDict[17] = []float64{0}
|
||||
dummyDict[18] = []float64{float64(privateLen), 0}
|
||||
dummyTopData := encodeCFFDict(dummyDict)
|
||||
topIdxSize := 2 + 1 + 8 + len(dummyTopData)
|
||||
dataStart := hdrSize + len(nameIndexData) + topIdxSize + len(stringIndexData) + len(globalSubrIndexData)
|
||||
charStringsPos := dataStart
|
||||
privatePos := charStringsPos + len(charStringsData)
|
||||
topDict[17] = []float64{float64(charStringsPos)}
|
||||
topDict[18] = []float64{float64(privateLen), float64(privatePos)}
|
||||
finalTopData := encodeCFFDict(topDict)
|
||||
topIndex := encodeCFFIndex([][]byte{finalTopData})
|
||||
newCFF.Write(data[:hdrSize])
|
||||
newCFF.Write(nameIndexData)
|
||||
newCFF.Write(topIndex)
|
||||
newCFF.Write(stringIndexData)
|
||||
newCFF.Write(globalSubrIndexData)
|
||||
if newCFF.Len() != dataStart {
|
||||
diff := newCFF.Len() - dataStart
|
||||
charStringsPos += diff
|
||||
privatePos += diff
|
||||
topDict[17] = []float64{float64(charStringsPos)}
|
||||
topDict[18] = []float64{float64(privateLen), float64(privatePos)}
|
||||
finalTopData = encodeCFFDict(topDict)
|
||||
topIndex = encodeCFFIndex([][]byte{finalTopData})
|
||||
newCFF.Reset()
|
||||
newCFF.Write(data[:hdrSize])
|
||||
newCFF.Write(nameIndexData)
|
||||
newCFF.Write(topIndex)
|
||||
newCFF.Write(stringIndexData)
|
||||
newCFF.Write(globalSubrIndexData)
|
||||
|
||||
// encodeSanitizedCFF 按字典编码长度收敛偏移并组装规范化字体
|
||||
// 入参: header 头部, names 名称索引, dict 顶层字典, strings 字符串索引, globals 全局子程序索引
|
||||
// 入参: chars 字形索引, private 私有字典, locals 局部子程序索引
|
||||
// 返回: []byte CFF字体数据
|
||||
func encodeSanitizedCFF(header, names []byte, dict cffDict, strings, globals, chars, private, locals []byte) []byte {
|
||||
dict[17] = []float64{0}
|
||||
dict[18] = []float64{float64(len(private)), 0}
|
||||
prefix := len(header) + len(names) + len(strings) + len(globals)
|
||||
var top []byte
|
||||
for {
|
||||
top = encodeCFFIndex([][]byte{encodeCFFDict(dict)})
|
||||
charOffset := prefix + len(top)
|
||||
privateOffset := charOffset + len(chars)
|
||||
if dict[17][0] == float64(charOffset) && dict[18][1] == float64(privateOffset) {
|
||||
break
|
||||
}
|
||||
newCFF.Write(charStringsData)
|
||||
newCFF.Write(finalPrivData)
|
||||
return newCFF.Bytes(), nil
|
||||
dict[17][0] = float64(charOffset)
|
||||
dict[18][1] = float64(privateOffset)
|
||||
}
|
||||
var out bytes.Buffer
|
||||
out.Grow(prefix + len(top) + len(chars) + len(private) + len(locals))
|
||||
for _, part := range [][]byte{header, names, top, strings, globals, chars, private, locals} {
|
||||
out.Write(part)
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
// parseCFFAndCountGlyphs 解析CFF头部并统计字形数量
|
||||
|
||||
@@ -24,31 +24,6 @@ import (
|
||||
// 坐标以左上角为原点,单位为毫米,子路径从Move开始,Close回到当前子路径起点
|
||||
type GeometryPath []GeometrySegment
|
||||
|
||||
// stationaryEndpoint 判断贝塞尔曲线端点是否具有零一阶导数
|
||||
// 返回: bool 是否存在驻点端点
|
||||
func (p GeometryPath) stationaryEndpoint() bool {
|
||||
var current, start Point
|
||||
for _, segment := range p {
|
||||
switch segment.Verb {
|
||||
case GeometryMove:
|
||||
start = segment.End
|
||||
case GeometryCubic:
|
||||
if segment.Control1 == current || segment.Control2 == segment.End {
|
||||
return true
|
||||
}
|
||||
case GeometryQuad:
|
||||
if segment.Control1 == current || segment.Control1 == segment.End {
|
||||
return true
|
||||
}
|
||||
case GeometryClose:
|
||||
current = start
|
||||
continue
|
||||
}
|
||||
current = segment.End
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GeometryVerb 表示几何路径指令
|
||||
type GeometryVerb uint8
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"runtime"
|
||||
|
||||
"github.com/xiaoqidun/pdfgo"
|
||||
)
|
||||
@@ -83,6 +84,7 @@ func (p *pdfImporter) compositingCache() *pdfCompositeCache {
|
||||
}
|
||||
|
||||
// coverage 复用已编译的覆盖场景,跳过与当前区域无交集的图元
|
||||
// 几何编译前让出调度,避免单线程运行时积累已失效的临时对象
|
||||
// 入参: node 图元, stroke 是否描边
|
||||
// 返回: image.Image 当前区域覆盖或空值, error 编译或渲染错误
|
||||
func (c *pdfCompositor) coverage(node pdfCompositeNode, stroke bool) (image.Image, error) {
|
||||
@@ -95,6 +97,7 @@ func (c *pdfCompositor) coverage(node pdfCompositeNode, stroke bool) (image.Imag
|
||||
key := pdfCompositeKey{path: node.path, text: node.text, image: node.image, stroke: stroke}
|
||||
geometry, ok := c.cache.geometry[key]
|
||||
if !ok {
|
||||
runtime.Gosched()
|
||||
var err error
|
||||
geometry.scene, geometry.box, err = c.importer.compileGroup(func(local *pdfImporter) error { return node.coverage(local, stroke) })
|
||||
if err != nil {
|
||||
|
||||
@@ -201,7 +201,7 @@ func (CanvasBackend) Combine(left, right GeometryPath, operation GeometryOperati
|
||||
return *geometryFromCanvasPath(result), nil
|
||||
}
|
||||
|
||||
// Stroke 将绝对长度虚线与描边转换为填充区域,驻点曲线按指定精度展开
|
||||
// Stroke 将绝对长度虚线与描边转换为填充区域,曲线按指定精度展开
|
||||
// 入参: path 页面路径, options 描边样式
|
||||
// 返回: GeometryPath 描边区域, error 样式或路径错误
|
||||
func (CanvasBackend) Stroke(path GeometryPath, options StrokeOptions) (GeometryPath, error) {
|
||||
@@ -223,9 +223,7 @@ func (CanvasBackend) Stroke(path GeometryPath, options StrokeOptions) (GeometryP
|
||||
style.applyLineJoin(options.Join, options.MiterLimit)
|
||||
style.lineCap = pathLineCap(options.Cap, style.lineCap)
|
||||
p = p.Dash(options.DashOffset, options.Dashes...)
|
||||
if path.stationaryEndpoint() {
|
||||
p = p.Flatten(tolerance)
|
||||
}
|
||||
p = p.Stroke(options.Width, style.lineCap, style.lineJoin, tolerance)
|
||||
return *geometryFromCanvasPath(p), nil
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user