mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 17:27:08 +08:00
1 parent
9b38f6f039
commit
444c01b976
6 files changed
+42
-42
No files matched your search
@@ -1391,7 +1391,7 @@ export class CanvasEditor {
|
||||
right = Math.max(right, x + advance * source.scale);
|
||||
bottom = Math.max(bottom, y + size - baseline);
|
||||
naturalX += advance;
|
||||
x += run.dx?.length ? run.dx[Math.min(index, run.dx.length - 1)] * PX_PER_MM : run.dy?.length ? 0 : advance * source.scale;
|
||||
x += run.dx?.length ? run.dx[Math.min(index, run.dx.length - 1)] * PX_PER_MM : 0;
|
||||
y += run.dy?.length ? run.dy[Math.min(index, run.dy.length - 1)] * PX_PER_MM : 0;
|
||||
index++;
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ func (e *Editor) RewriteText(obj *TextObject, value string) error {
|
||||
unit *= obj.HScale
|
||||
}
|
||||
advance := func(char rune) float64 { return float64(sfnt.GlyphAdvance(sfnt.GlyphIndex(char))) * unit }
|
||||
if codes[i].DeltaX != "" || codes[i].DeltaY == "" {
|
||||
if codes[i].DeltaX != "" {
|
||||
codes[i].DeltaX = rewriteTextDeltas(codes[i].DeltaX, runes, replacement, from, to, advance)
|
||||
}
|
||||
if codes[i].DeltaY != "" {
|
||||
|
||||
+30
-4
@@ -22,6 +22,12 @@ type ObjectContour struct {
|
||||
EvenOdd bool `json:"evenOdd,omitempty"`
|
||||
}
|
||||
|
||||
// annotationMeasurement 按注解绘制顺序收集对象范围和轮廓
|
||||
type annotationMeasurement struct {
|
||||
renderer *Renderer
|
||||
result ObjectMeasurement
|
||||
}
|
||||
|
||||
// ObjectBounds 获取文字、路径、图片或复合对象在页面坐标中的轴对齐范围,不修改对象
|
||||
// 文字采用字形范围,路径包含描边与裁剪,图片采用裁剪后的几何范围,不解码像素或排除透明像素
|
||||
// 底纹按填充或描边轮廓度量,不展开图案单元
|
||||
@@ -38,11 +44,11 @@ func (r *Renderer) ObjectBounds(object GraphicObject, drawParam string) (Box, er
|
||||
// 入参: annotation 注解
|
||||
// 返回: Box 页面范围, []ObjectContour 命中轮廓, error 错误信息
|
||||
func (r *Renderer) AnnotationGeometry(annotation Annotation) (Box, []ObjectContour, error) {
|
||||
object := GraphicObject{Type: "CompositeObject", CompositeGraphicUnit: CompositeGraphicUnit{Boundary: annotation.Appearance.Boundary, Objects: annotation.Appearance.Objects}}
|
||||
box, contours, err := r.ObjectGeometry(object, "")
|
||||
if err != nil {
|
||||
return box, contours, err
|
||||
measurement := &annotationMeasurement{renderer: r}
|
||||
if err := r.walkAnnotation(annotation, measurement); err != nil {
|
||||
return Box{}, nil, err
|
||||
}
|
||||
box, contours := measurement.result.Bounds, measurement.result.Contours
|
||||
for _, source := range r.annotationActionSources([]Annotation{annotation}) {
|
||||
for _, action := range source.Actions {
|
||||
if action.Event != "CLICK" {
|
||||
@@ -65,6 +71,26 @@ func (r *Renderer) AnnotationGeometry(annotation Annotation) (Box, []ObjectConto
|
||||
return box, contours, nil
|
||||
}
|
||||
|
||||
// DrawObject 使用对象实际继承状态度量注解内容,不附加容器裁剪
|
||||
// 入参: object 图形对象, state 绘制状态
|
||||
// 返回: error 度量错误
|
||||
func (m *annotationMeasurement) DrawObject(object *GraphicObject, state RenderState) error {
|
||||
result, err := m.renderer.MeasureObject(*object, MeasureOptions{Defaults: state.Defaults, Parent: state.Parent, BoundaryInCTM: state.BoundaryInCTM, Clip: state.Clip, Contours: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.result.Bounds = unionTextBox(m.result.Bounds, result.Bounds)
|
||||
m.result.Contours = append(m.result.Contours, result.Contours...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawStamp 满足页面访问接口,注解外观遍历不包含数字签章
|
||||
// 入参: stamp 数字签章
|
||||
// 返回: error 始终为空
|
||||
func (m *annotationMeasurement) DrawStamp(stamp Stamp) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ObjectContours 获取路径、图片或复合对象的实际绘制区域,包含描边、虚线、变换及对象裁剪
|
||||
// 图片不解码像素,底纹不展开图案单元;不包含页面边界或父级变换
|
||||
// 入参: object 路径、图片或复合对象, drawParam 图层绘制参数标识
|
||||
|
||||
@@ -171,15 +171,11 @@ func (r *Renderer) PositionText(object TextObject, state RenderState) (*Position
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
positioner := NewTextPositioner(code, object.ReadDirection)
|
||||
positioner := NewTextPositioner(code)
|
||||
for i, gid := range ids {
|
||||
path := paths[i]
|
||||
width := float64(metrics.GlyphAdvance(gid)) * unit
|
||||
advance := width * horizontal
|
||||
if (object.ReadDirection-object.CharDirection)%180 != 0 {
|
||||
advance = size
|
||||
}
|
||||
origin := positioner.Next(advance)
|
||||
origin := positioner.Next()
|
||||
scaleX := horizontal
|
||||
if bold && len(path) > 0 {
|
||||
outline, err := geometry.Stroke(path, StrokeOptions{Width: size * 0.04, Join: "Round", Cap: "Round"})
|
||||
|
||||
@@ -203,7 +203,6 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
glyphTransforms := r.textObjectGlyphTransforms(fontID, obj)
|
||||
hasUnderline := strings.Contains(obj.Decoration, "Underline")
|
||||
_, shadedFill := fillPaint.(canvas.Gradient)
|
||||
verticalAdvance := (obj.ReadDirection-obj.CharDirection)%180 != 0
|
||||
codePos := 0
|
||||
textPos := 0
|
||||
for index, tc := range obj.TextCode {
|
||||
@@ -223,7 +222,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
if textRun != nil && tc.Index == "" {
|
||||
spans = textGlyphSpans(runes, glyphTransforms, codePos)
|
||||
}
|
||||
positioner := NewTextPositioner(tc, obj.ReadDirection)
|
||||
positioner := NewTextPositioner(tc)
|
||||
xs, ys := positioner.xs, positioner.ys
|
||||
drawAsPath := embeddedFont || face.FauxBold > 0 || textCodePositioned(tc, xs, ys) || fillClip != nil || shadedFill || fillPattern != nil || shouldStroke
|
||||
for i, glyph := range glyphs {
|
||||
@@ -244,11 +243,7 @@ func (r *Renderer) renderText(ctx *canvas.Context, obj TextObject, pageH float64
|
||||
} else {
|
||||
glyphWidth = textGlyphWidth(face, glyph)
|
||||
}
|
||||
advance := glyphWidth * hScale
|
||||
if verticalAdvance {
|
||||
advance = sizeMM
|
||||
}
|
||||
position := positioner.Next(advance)
|
||||
position := positioner.Next()
|
||||
cx, cy := position.X, position.Y
|
||||
var canvasX, canvasY float64
|
||||
if boundaryInCTM && parentCTM != nil {
|
||||
|
||||
+6
-23
@@ -19,9 +19,7 @@ import "slices"
|
||||
// TextPositioner 按OFD坐标及增量定位连续字形,不依赖字体或绘图库
|
||||
type TextPositioner struct {
|
||||
xs, ys, dxs, dys []float64
|
||||
direction Point
|
||||
current Point
|
||||
previous float64
|
||||
index int
|
||||
}
|
||||
|
||||
@@ -54,34 +52,22 @@ func textCodeOrigins(codes []TextCode, ascent float64) []TextCode {
|
||||
return codes
|
||||
}
|
||||
|
||||
// NewTextPositioner 创建单个TextCode的定位器,方向采用OFD角度
|
||||
// 入参: code 文本定位数据, direction 阅读方向
|
||||
// NewTextPositioner 创建单个TextCode的定位器,缺省位移不改变字形原点
|
||||
// 入参: code 文本定位数据
|
||||
// 返回: TextPositioner 字形定位器
|
||||
func NewTextPositioner(code TextCode, direction int) TextPositioner {
|
||||
result := TextPositioner{xs: parseFloats(code.X), ys: parseFloats(code.Y), dxs: parseFloats(code.DeltaX), dys: parseFloats(code.DeltaY), direction: Point{X: 1}}
|
||||
switch direction {
|
||||
case 90:
|
||||
result.direction = Point{Y: 1}
|
||||
case 180:
|
||||
result.direction = Point{X: -1}
|
||||
case 270:
|
||||
result.direction = Point{Y: -1}
|
||||
}
|
||||
return result
|
||||
func NewTextPositioner(code TextCode) TextPositioner {
|
||||
return TextPositioner{xs: parseFloats(code.X), ys: parseFloats(code.Y), dxs: parseFloats(code.DeltaX), dys: parseFloats(code.DeltaY)}
|
||||
}
|
||||
|
||||
// Next 返回下一个字形位置并保存其推进长度,显式坐标和增量优先
|
||||
// 入参: advance 当前字形推进长度,单位为毫米
|
||||
// Next 根据坐标与位移返回下一个字形原点
|
||||
// 返回: Point 字形原点
|
||||
func (p *TextPositioner) Next(advance float64) Point {
|
||||
func (p *TextPositioner) Next() Point {
|
||||
i := p.index
|
||||
if i < len(p.xs) {
|
||||
p.current.X = p.xs[i]
|
||||
} else if i > 0 {
|
||||
if dx, ok := textDelta(p.dxs, i-1); ok {
|
||||
p.current.X += dx
|
||||
} else if len(p.dys) == 0 {
|
||||
p.current.X += p.previous * p.direction.X
|
||||
}
|
||||
}
|
||||
if i < len(p.ys) {
|
||||
@@ -89,11 +75,8 @@ func (p *TextPositioner) Next(advance float64) Point {
|
||||
} else if i > 0 {
|
||||
if dy, ok := textDelta(p.dys, i-1); ok {
|
||||
p.current.Y += dy
|
||||
} else if len(p.dxs) == 0 {
|
||||
p.current.Y += p.previous * p.direction.Y
|
||||
}
|
||||
}
|
||||
p.previous = advance
|
||||
p.index++
|
||||
return p.current
|
||||
}
|
||||
Reference in new issue
Block a user