From f322daa616492180a0c1936e7dc0d10019bcde76 Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Mon, 26 Oct 2026 21:47:19 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E7=BC=96=E8=BE=91=E8=83=BD=E5=8A=9B):=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=88=E5=AD=97=E7=BC=96=E8=BE=91=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/webui/ofdgo.css | 1 + assets/webui/ofdgo_edit.js | 16 +++++++++---- internal/webui/webui.go | 47 +++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 22 deletions(-) diff --git a/assets/webui/ofdgo.css b/assets/webui/ofdgo.css index 9b95444..6aafcdf 100644 --- a/assets/webui/ofdgo.css +++ b/assets/webui/ofdgo.css @@ -3279,6 +3279,7 @@ button.outline-link:hover { .edit-text span[data-glyph]::after { content: attr(data-glyph); + letter-spacing: 0; position: absolute; left: 0; top: 0; diff --git a/assets/webui/ofdgo_edit.js b/assets/webui/ofdgo_edit.js index 35f24dc..e44fc46 100644 --- a/assets/webui/ofdgo_edit.js +++ b/assets/webui/ofdgo_edit.js @@ -1851,12 +1851,15 @@ export class CanvasEditor { right = Math.max(right, x); bottom = Math.max(bottom, y + lineHeight - baseline); } - for (const char of run.text) { + const chars = [...run.text]; + while (index < chars.length) { + const count = source.glyphCounts?.[glyphIndex] || 1; + const char = chars.slice(index, index + count).join(""); x = run.positions[index][0] * unit; y = run.positions[index][1] * unit; const span = document.createElement("span"), node = document.createTextNode(char); span.append(node); - const glyph = source.glyphs?.[glyphIndex++]; + const glyph = source.glyphs?.[glyphIndex]; if (glyph) { span.dataset.glyph = glyph; glyphPositions.push({ span, start: position, end: position + char.length }); @@ -1869,7 +1872,11 @@ export class CanvasEditor { positions.push({ node, start: position, end: position + char.length }); position += char.length; const advance = context.measureText(glyph || char).width; - if (glyph) span.style.width = `${advance}px`; + if (glyph) { + const naturalAdvance = [...char].reduce((width, value) => width + context.measureText(value).width, 0); + span.style.width = `${advance}px`; + span.style.letterSpacing = `${(advance - naturalAdvance) / count}px`; + } for (const dx of [0, advance * scale]) { for (const dy of [-baseline * vscale, (lineHeight - baseline) * vscale]) { const px = x + dx * cos - dy * sin, py = y + dx * sin + dy * cos; @@ -1880,7 +1887,8 @@ export class CanvasEditor { } } naturalX += advance; - index++; + index += count; + glyphIndex += count; } if (!line.hasChildNodes()) { const node = document.createTextNode(""); diff --git a/internal/webui/webui.go b/internal/webui/webui.go index 957ac4a..9fedff9 100644 --- a/internal/webui/webui.go +++ b/internal/webui/webui.go @@ -4353,9 +4353,28 @@ func (e *businessEngine) previewText(args []platformValue) (any, error) { } else if err := editor.RewriteTextWithFont(&text, args[2].String(), fontID); err != nil { return nil, err } + glyphs := make(map[int]string) + glyphCounts := make(map[int]int) + if fontID == "" { + for _, transform := range text.CGTransform { + if transform.CodeCount != transform.GlyphCount && transform.GlyphCount != 1 { + continue + } + for i, value := range strings.Fields(transform.Glyphs) { + glyph, err := strconv.ParseUint(value, 10, 16) + if err != nil { + return nil, err + } + glyphs[transform.CodePosition+i] = string(rune(0xF0000 + glyph)) + } + if transform.GlyphCount == 1 && transform.CodeCount > 1 { + glyphCounts[transform.CodePosition] = transform.CodeCount + } + } + } var runs []any var x, y float64 - value, offset := []rune(text.Text()), 0 + value, offset, glyphOffset := []rune(text.Text()), 0, 0 for _, code := range text.TextCode { if code.X != "" { x, _ = strconv.ParseFloat(code.X, 64) @@ -4371,10 +4390,17 @@ func (e *businessEngine) previewText(args []platformValue) (any, error) { code.X, code.Y = fmt.Sprint(x), fmt.Sprint(y) positioner := ofdgo.NewTextPositioner(code) positions := make([][2]float64, len([]rune(content))) + var point ofdgo.Point + remaining := 0 for i := range positions { - point := positioner.Next() + if remaining == 0 { + point = positioner.Next() + remaining = max(1, glyphCounts[glyphOffset+i]) + } positions[i] = [2]float64{point.X, point.Y} + remaining-- } + glyphOffset += len(positions) runs = append(runs, map[string]any{"text": content, "x": x, "y": y, "positions": positions, "line": line}) offset += len(positions) } @@ -4382,22 +4408,7 @@ func (e *businessEngine) previewText(args []platformValue) (any, error) { if scale == 0 { scale = 1 } - glyphs := make(map[int]string) - if fontID == "" { - for _, transform := range text.CGTransform { - if transform.CodeCount != transform.GlyphCount { - continue - } - for i, value := range strings.Fields(transform.Glyphs) { - glyph, err := strconv.ParseUint(value, 10, 16) - if err != nil { - return nil, err - } - glyphs[transform.CodePosition+i] = string(rune(0xF0000 + glyph)) - } - } - } - return map[string]any{"runs": runs, "glyphs": glyphs, "scale": scale, "vscale": text.VScale, "rotation": text.CharDirection, "weight": text.Weight, "italic": text.Italic}, nil + return map[string]any{"runs": runs, "glyphs": glyphs, "glyphCounts": glyphCounts, "scale": scale, "vscale": text.VScale, "rotation": text.CharDirection, "weight": text.Weight, "italic": text.Italic}, nil } // readTextSelection 将画布路径转换为同一容器的库层文字选区