diff --git a/go.mod b/go.mod index 2baf35d3..66525b3f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/go-gl/gl v0.0.0-20231021071112-07e5d0ea2e71 // indirect github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a // indirect github.com/go-text/render v0.2.0 // indirect - github.com/go-text/typesetting v0.2.0 // indirect + github.com/go-text/typesetting v0.2.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/jeandeaual/go-locale v0.0.0-20241204123234-32dda1c00a20 // indirect diff --git a/go.sum b/go.sum index b46e5a57..f6245480 100644 --- a/go.sum +++ b/go.sum @@ -97,10 +97,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a h1:vxnBhFDDT+ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20240506104042-037f3cc74f2a/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-text/render v0.2.0 h1:LBYoTmp5jYiJ4NPqDc2pz17MLmA3wHw1dZSVGcOdeAc= github.com/go-text/render v0.2.0/go.mod h1:CkiqfukRGKJA5vZZISkjSYrcdtgKQWRa2HIzvwNN5SU= -github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= -github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66 h1:GUrm65PQPlhFSKjLPGOZNPNxLCybjzjYBzjfoBGaDUY= -github.com/go-text/typesetting-utils v0.0.0-20240317173224-1986cbe96c66/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= +github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8= +github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= +github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/vendor/github.com/go-text/typesetting/font/metrics.go b/vendor/github.com/go-text/typesetting/font/metrics.go index 9c626d64..dd86aaee 100644 --- a/vendor/github.com/go-text/typesetting/font/metrics.go +++ b/vendor/github.com/go-text/typesetting/font/metrics.go @@ -130,6 +130,19 @@ var ( tagCapHeight = ot.MustNewTag("cpht") ) +// return the height from baseline (in font units) +func (f *Face) runeHeight(r rune) float32 { + gid, ok := f.Font.NominalGlyph(r) + if !ok { + return 0 + } + extents, ok := f.GlyphExtents(gid) + if !ok { + return 0 + } + return extents.YBearing +} + // LineMetric returns the metric identified by `metric` (in fonts units). func (f *Face) LineMetric(metric LineMetric) float32 { switch metric { @@ -152,8 +165,18 @@ func (f *Face) LineMetric(metric LineMetric) float32 { case SubscriptEmXOffset: return float32(f.os2.ySubscriptXOffset) + f.mvar.getVar(tagSubscriptXOffset, f.coords) case CapHeight: + if f.os2.version < 2 { + // sCapHeight may be set equal to the top of the unscaled and unhinted glyph + // bounding box of the glyph encoded at U+0048 (LATIN CAPITAL LETTER H). + return f.runeHeight('H') + } return float32(f.os2.sCapHeight) + f.mvar.getVar(tagCapHeight, f.coords) case XHeight: + if f.os2.version < 2 { + // sxHeight equal to the top of the unscaled and unhinted glyph bounding box + // of the glyph encoded at U+0078 (LATIN SMALL LETTER X). + return f.runeHeight('x') + } return float32(f.os2.sxHeigh) + f.mvar.getVar(tagXHeight, f.coords) default: return 0 diff --git a/vendor/github.com/go-text/typesetting/font/opentype/tables/name_src.go b/vendor/github.com/go-text/typesetting/font/opentype/tables/name_src.go index 3a14443d..15e54f76 100644 --- a/vendor/github.com/go-text/typesetting/font/opentype/tables/name_src.go +++ b/vendor/github.com/go-text/typesetting/font/opentype/tables/name_src.go @@ -140,8 +140,9 @@ func (names Name) decodeRecord(n nameRecord) string { } value := names.stringData[n.stringOffset:end] - if n.platformID == PlatformUnicode || (n.platformID == PlatformMicrosoft && - n.encodingID == PEMicrosoftUnicodeCs) { + if n.platformID == PlatformUnicode || + (n.platformID == PlatformMicrosoft && + (n.encodingID == PEMicrosoftUnicodeCs || n.encodingID == PEMicrosoftUcs4 || n.encodingID == PEMicrosoftSymbolCs)) { return decodeUtf16(value) } diff --git a/vendor/github.com/go-text/typesetting/fontscan/serialize.go b/vendor/github.com/go-text/typesetting/fontscan/serialize.go index 2e823833..3e118e94 100644 --- a/vendor/github.com/go-text/typesetting/fontscan/serialize.go +++ b/vendor/github.com/go-text/typesetting/fontscan/serialize.go @@ -185,7 +185,7 @@ func (ff *fileFootprints) deserializeFrom(src []byte) error { return nil } -const cacheFormatVersion = 4 +const cacheFormatVersion = 5 func max(i, j int) int { if i > j { diff --git a/vendor/github.com/go-text/typesetting/shaping/output.go b/vendor/github.com/go-text/typesetting/shaping/output.go index a942bb3f..236d9de5 100644 --- a/vendor/github.com/go-text/typesetting/shaping/output.go +++ b/vendor/github.com/go-text/typesetting/shaping/output.go @@ -146,6 +146,11 @@ type Output struct { // the output in order to render each run in a multi-font sequence in the // correct font. Face *font.Face + + // VisualIndex is the visual position of this run within its containing line where + // 0 indicates the leftmost run and increasing values move to the right. This is + // useful for sorting the runs for drawing purposes. + VisualIndex int32 } // ToFontUnit converts a metrics (typically found in [Glyph] fields) @@ -179,16 +184,24 @@ func (o *Output) RecomputeAdvance() { // advanceSpaceAware adjust the value in [Advance] // if a white space character ends the run. // Any end letter spacing (on the last glyph) is also removed +// The paragraphDir is the text direction of the overall paragraph containing o. +// If the paragraphDir is different then o's Direction, this method has no effect +// because the trailing space in this run will always be internal to the paragraph. // // TODO: should we take into account multiple spaces ? -func (o *Output) advanceSpaceAware() fixed.Int26_6 { +func (o *Output) advanceSpaceAware(paragraphDir di.Direction) fixed.Int26_6 { L := len(o.Glyphs) - if L == 0 { + if L == 0 || paragraphDir != o.Direction { return o.Advance } // adjust the last to account for spaces - lastG := o.Glyphs[L-1] + var lastG Glyph + if o.Direction.Progression() == di.FromTopLeft { + lastG = o.Glyphs[L-1] + } else { + lastG = o.Glyphs[0] + } if o.Direction.IsVertical() { if lastG.Height == 0 { return o.Advance - lastG.YAdvance diff --git a/vendor/github.com/go-text/typesetting/shaping/spacing.go b/vendor/github.com/go-text/typesetting/shaping/spacing.go index cc16c345..f3c1593f 100644 --- a/vendor/github.com/go-text/typesetting/shaping/spacing.go +++ b/vendor/github.com/go-text/typesetting/shaping/spacing.go @@ -10,7 +10,7 @@ import ( // Note that space is always added, even on boundaries. // // See also the convenience function [AddSpacing] to handle a slice of runs. - +// // See also https://www.w3.org/TR/css-text-3/#word-separator func (run *Output) AddWordSpacing(text []rune, additionalSpacing fixed.Int26_6) { isVertical := run.Direction.IsVertical() diff --git a/vendor/github.com/go-text/typesetting/shaping/wrapping.go b/vendor/github.com/go-text/typesetting/shaping/wrapping.go index a7e1bc0e..65375773 100644 --- a/vendor/github.com/go-text/typesetting/shaping/wrapping.go +++ b/vendor/github.com/go-text/typesetting/shaping/wrapping.go @@ -414,6 +414,10 @@ type Line []Output // WrapConfig provides line-wrapper settings. type WrapConfig struct { + // Direction describes the text layout of the overall paragraph, rather than + // individual runs of text. This is used to compute the correct visual order of + // bidirectional text runs. + Direction di.Direction // TruncateAfterLines is the number of lines of text to allow before truncating // the text. A value of zero means no limit. TruncateAfterLines int @@ -433,6 +437,11 @@ type WrapConfig struct { // breaking in between UAX#14 line breaking candidates, or "within words" in // many scripts. BreakPolicy LineBreakPolicy + // DisableTrailingWhitespaceTrim turns off a feature that automatically sets the + // advance of trailing whitespace on a line to zero. In display contexts, you + // usually want this feature enabled, but for text editors it is frequently + // desirable to allow trailing whitespace to occupy space itself. + DisableTrailingWhitespaceTrim bool } // LineBreakPolicy specifies when considering a line break within a "word" or UAX#14 @@ -839,28 +848,81 @@ type WrappedLine struct { NextLine int } +// swapVisualOrder inverts the visual index of runs in [subline], by swapping pairs of visual indices across the midpoint +// of the slice. +func swapVisualOrder(subline Line) { + L := len(subline) + for i := range subline[0 : L/2] { + j := (L - i) - 1 + subline[i].VisualIndex, subline[j].VisualIndex = subline[j].VisualIndex, subline[i].VisualIndex + } +} + +// computeBidiOrdering resolves the [VisualIndex] of each run. +func computeBidiOrdering(dir di.Direction, finalLine Line) { + bidiStart := -1 + for idx, run := range finalLine { + basePosition := idx + if dir.Progression() == di.TowardTopLeft { + basePosition = len(finalLine) - 1 - idx + } + finalLine[idx].VisualIndex = int32(basePosition) + if run.Direction == dir { + if bidiStart != -1 { + swapVisualOrder(finalLine[bidiStart:idx]) + bidiStart = -1 + } + } else if bidiStart == -1 { + bidiStart = idx + } + } + if bidiStart != -1 { + swapVisualOrder(finalLine[bidiStart:]) + } +} + func (l *LineWrapper) postProcessLine(finalLine Line, done bool) (WrappedLine, bool) { if len(finalLine) > 0 { - finalRun := finalLine[len(finalLine)-1] - - // zero trailing whitespace advance, - // to be coherent with Output.advanceSpaceAware - if L := len(finalRun.Glyphs); L != 0 { - g := &finalRun.Glyphs[L-1] - if finalRun.Direction.IsVertical() { - if g.Height == 0 { - g.YAdvance = 0 + computeBidiOrdering(l.config.Direction, finalLine) + if !l.config.DisableTrailingWhitespaceTrim { + // Here we find the last visual run in the line. + goalIdx := len(finalLine) - 1 + if l.config.Direction.Progression() == di.TowardTopLeft { + goalIdx = 0 + } + for logicalIdx, run := range finalLine { + if run.VisualIndex == int32(goalIdx) { + goalIdx = logicalIdx + break } - } else { // horizontal - if g.Width == 0 { - g.XAdvance = 0 + } + // This next block locates the first/last visual glyph on the line and + // zeroes its advance if it is whitespace. + finalVisualRun := &finalLine[goalIdx] + var finalVisualGlyph *Glyph + if L := len(finalVisualRun.Glyphs); L > 0 { + if l.config.Direction.Progression() == di.FromTopLeft { + finalVisualGlyph = &finalVisualRun.Glyphs[L-1] + } else { + finalVisualGlyph = &finalVisualRun.Glyphs[0] + } + + if finalVisualRun.Direction.IsVertical() { + if finalVisualGlyph.Height == 0 { + finalVisualGlyph.YAdvance = 0 + } + } else { // horizontal + if finalVisualGlyph.Width == 0 { + finalVisualGlyph.XAdvance = 0 + } } + finalVisualRun.RecomputeAdvance() } - finalRun.RecomputeAdvance() } + finalLogicalRun := finalLine[len(finalLine)-1] // Update the start position of the next line. - l.lineStartRune = finalRun.Runes.Count + finalRun.Runes.Offset + l.lineStartRune = finalLogicalRun.Runes.Count + finalLogicalRun.Runes.Offset } // Check whether we've exhausted the text. @@ -1103,7 +1165,7 @@ func (l *LineWrapper) processBreakOption(option breakOption, config lineConfig) } isFirstInLine := l.scratch.candidateLen() == 0 candidateRun := cutRun(run, l.mapper.mapping, l.lineStartRune, option.breakAtRune, isFirstInLine) - candidateLineWidth := (candidateRun.advanceSpaceAware() + l.scratch.candidateAdvance()).Ceil() + candidateLineWidth := (candidateRun.advanceSpaceAware(l.config.Direction) + l.scratch.candidateAdvance()).Ceil() if candidateLineWidth > config.maxWidth { // The run doesn't fit on the line. if !l.scratch.hasBest() { diff --git a/vendor/modules.txt b/vendor/modules.txt index 655103f6..84a1b60f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -92,7 +92,7 @@ github.com/go-gl/glfw/v3.3/glfw/glfw/src # github.com/go-text/render v0.2.0 ## explicit; go 1.17 github.com/go-text/render -# github.com/go-text/typesetting v0.2.0 +# github.com/go-text/typesetting v0.2.1 ## explicit; go 1.17 github.com/go-text/typesetting/di github.com/go-text/typesetting/font