diff --git a/text/gotext.go b/text/gotext.go index 14a053b5..c7ee3480 100644 --- a/text/gotext.go +++ b/text/gotext.go @@ -490,6 +490,7 @@ func wrapPolicyToGoText(p WrapPolicy) shaping.LineBreakPolicy { // shapeAndWrapText invokes the text shaper and returns wrapped lines in the shaper's native format. func (s *shaperImpl) shapeAndWrapText(params Parameters, txt []rune) (_ []shaping.Line, truncated int) { wc := shaping.WrapConfig{ + Direction: mapDirection(params.Locale.Direction), TruncateAfterLines: params.MaxLines, TextContinues: params.forceTruncate, BreakPolicy: wrapPolicyToGoText(params.WrapPolicy), @@ -871,8 +872,9 @@ func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirec return line{} } line := line{ - runs: make([]runLayout, len(o)), - direction: dir, + runs: make([]runLayout, len(o)), + direction: dir, + visualOrder: make([]int, len(o)), } maxSize := fixed.Int26_6(0) for i := range o { @@ -890,11 +892,13 @@ func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirec Count: run.Runes.Count, Offset: line.runeCount, }, - Direction: unmapDirection(run.Direction), - face: run.Face, - Advance: run.Advance, - PPEM: run.Size, + Direction: unmapDirection(run.Direction), + face: run.Face, + Advance: run.Advance, + PPEM: run.Size, + VisualPosition: int(run.VisualIndex), } + line.visualOrder[run.VisualIndex] = i line.runeCount += run.Runes.Count line.width += run.Advance if line.ascent < run.LineBounds.Ascent { @@ -905,64 +909,11 @@ func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirec } } line.lineHeight = maxSize - computeVisualOrder(&line) - return line -} - -// computeVisualOrder will populate the Line's VisualOrder field and the -// VisualPosition field of each element in Runs. -func computeVisualOrder(l *line) { - l.visualOrder = make([]int, len(l.runs)) - const none = -1 - bidiRangeStart := none - - // visPos returns the visual position for an individual logically-indexed - // run in this line, taking only the line's overall text direction into - // account. - visPos := func(logicalIndex int) int { - if l.direction.Progression() == system.TowardOrigin { - return len(l.runs) - 1 - logicalIndex - } - return logicalIndex - } - - // resolveBidi populated the line's VisualOrder fields for the elements in the - // half-open range [bidiRangeStart:bidiRangeEnd) indicating that those elements - // should be displayed in reverse-visual order. - resolveBidi := func(bidiRangeStart, bidiRangeEnd int) { - firstVisual := bidiRangeEnd - 1 - // Just found the end of a bidi range. - for startIdx := bidiRangeStart; startIdx < bidiRangeEnd; startIdx++ { - pos := visPos(firstVisual) - l.runs[startIdx].VisualPosition = pos - l.visualOrder[pos] = startIdx - firstVisual-- - } - bidiRangeStart = none - } - for runIdx, run := range l.runs { - if run.Direction.Progression() != l.direction.Progression() { - if bidiRangeStart == none { - bidiRangeStart = runIdx - } - continue - } else if bidiRangeStart != none { - // Just found the end of a bidi range. - resolveBidi(bidiRangeStart, runIdx) - bidiRangeStart = none - } - pos := visPos(runIdx) - l.runs[runIdx].VisualPosition = pos - l.visualOrder[pos] = runIdx - } - if bidiRangeStart != none { - // We ended iteration within a bidi segment, resolve it. - resolveBidi(bidiRangeStart, len(l.runs)) - } // Iterate and resolve the X of each run. x := fixed.Int26_6(0) - for _, runIdx := range l.visualOrder { - l.runs[runIdx].X = x - x += l.runs[runIdx].Advance + for _, runIdx := range line.visualOrder { + line.runs[runIdx].X = x + x += line.runs[runIdx].Advance } + return line } diff --git a/text/gotext_test.go b/text/gotext_test.go index 2ed5f9b7..00c662e3 100644 --- a/text/gotext_test.go +++ b/text/gotext_test.go @@ -3,7 +3,6 @@ package text import ( "fmt" "math" - "reflect" "strconv" "testing" @@ -450,120 +449,6 @@ func TestToLine(t *testing.T) { } } -func TestComputeVisualOrder(t *testing.T) { - type testcase struct { - name string - input line - expectedVisualOrder []int - } - for _, tc := range []testcase{ - { - name: "ltr", - input: line{ - direction: system.LTR, - runs: []runLayout{ - {Direction: system.LTR}, - {Direction: system.LTR}, - {Direction: system.LTR}, - }, - }, - expectedVisualOrder: []int{0, 1, 2}, - }, - { - name: "rtl", - input: line{ - direction: system.RTL, - runs: []runLayout{ - {Direction: system.RTL}, - {Direction: system.RTL}, - {Direction: system.RTL}, - }, - }, - expectedVisualOrder: []int{2, 1, 0}, - }, - { - name: "bidi-ltr", - input: line{ - direction: system.LTR, - runs: []runLayout{ - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.RTL}, - {Direction: system.RTL}, - {Direction: system.LTR}, - }, - }, - expectedVisualOrder: []int{0, 3, 2, 1, 4}, - }, - { - name: "bidi-ltr-complex", - input: line{ - direction: system.LTR, - runs: []runLayout{ - {Direction: system.RTL}, - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.RTL}, - }, - }, - expectedVisualOrder: []int{1, 0, 2, 4, 3, 5, 7, 6, 8, 10, 9}, - }, - { - name: "bidi-rtl", - input: line{ - direction: system.RTL, - runs: []runLayout{ - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.LTR}, - {Direction: system.LTR}, - {Direction: system.RTL}, - }, - }, - expectedVisualOrder: []int{4, 1, 2, 3, 0}, - }, - { - name: "bidi-rtl-complex", - input: line{ - direction: system.RTL, - runs: []runLayout{ - {Direction: system.LTR}, - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.LTR}, - {Direction: system.RTL}, - {Direction: system.LTR}, - {Direction: system.LTR}, - }, - }, - expectedVisualOrder: []int{9, 10, 8, 6, 7, 5, 3, 4, 2, 0, 1}, - }, - } { - t.Run(tc.name, func(t *testing.T) { - computeVisualOrder(&tc.input) - if !reflect.DeepEqual(tc.input.visualOrder, tc.expectedVisualOrder) { - t.Errorf("expected visual order %v, got %v", tc.expectedVisualOrder, tc.input.visualOrder) - } - for i, visualIndex := range tc.input.visualOrder { - if pos := tc.input.runs[visualIndex].VisualPosition; pos != i { - t.Errorf("line.VisualOrder[%d]=%d, but line.Runs[%d].VisualPosition=%d", i, visualIndex, visualIndex, pos) - } - } - }) - } -} - func FuzzLayout(f *testing.F) { ltrFace, _ := opentype.Parse(goregular.TTF) rtlFace, _ := opentype.Parse(nsareg.TTF)