mirror of
https://github.com/aljazceru/njump.git
synced 2025-12-17 14:24:27 +01:00
use emojiBuffer as the rule and make mainBuffer adapt to it, removing glyphs as necessary.
This commit is contained in:
2
go.mod
2
go.mod
@@ -77,5 +77,3 @@ require (
|
|||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/nbd-wtf/emoji => /home/fiatjaf/comp/emoji
|
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -152,8 +152,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
|
|||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw=
|
github.com/microcosm-cc/bluemonday v1.0.24 h1:NGQoPtwGVcbGkKfvyYk1yRqknzBuoMiUrO6R7uFTPlw=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8=
|
github.com/microcosm-cc/bluemonday v1.0.24/go.mod h1:ArQySAMps0790cHSkdPEJ7bGkF2VePWH773hsJNSHf8=
|
||||||
github.com/nbd-wtf/emoji v0.0.2 h1:5Bgkh7JOvnW5yAo+QPyFrtXLtPKr4vBgs9RJWlCX2Ho=
|
github.com/nbd-wtf/emoji v0.0.3 h1:YtkT7MVPXvqU1SQjvC/CShlWexnREzqNCxmhUnL00CA=
|
||||||
github.com/nbd-wtf/emoji v0.0.2/go.mod h1:tS6D9iI34qwBmWc5g8X7tVDkWXulqbTJRsvsM6QsS88=
|
github.com/nbd-wtf/emoji v0.0.3/go.mod h1:tS6D9iI34qwBmWc5g8X7tVDkWXulqbTJRsvsM6QsS88=
|
||||||
github.com/nbd-wtf/go-nostr v0.27.3 h1:u9fdP5h+Ap3LcDFD2j6F2buU/xOM9ddMY0LCDcC6ZyY=
|
github.com/nbd-wtf/go-nostr v0.27.3 h1:u9fdP5h+Ap3LcDFD2j6F2buU/xOM9ddMY0LCDcC6ZyY=
|
||||||
github.com/nbd-wtf/go-nostr v0.27.3/go.mod h1:e5WOFsKEpslDOxIgK00NqemH7KuAvKIW6sBXeJYCfUs=
|
github.com/nbd-wtf/go-nostr v0.27.3/go.mod h1:e5WOFsKEpslDOxIgK00NqemH7KuAvKIW6sBXeJYCfUs=
|
||||||
github.com/nbd-wtf/nostr-sdk v0.0.4 h1:vMCiYpFElKMHPXpZjFVEq4utoTLdTYbkqXVYH1/4uzs=
|
github.com/nbd-wtf/nostr-sdk v0.0.4 h1:vMCiYpFElKMHPXpZjFVEq4utoTLdTYbkqXVYH1/4uzs=
|
||||||
|
|||||||
167
image_utils.go
167
image_utils.go
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
@@ -414,61 +413,73 @@ func shapeText(rawText []rune, fontSize int) (shaping.Output, []bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this will be used to determine whether a given glyph is an emoji or not when rendering
|
// this will be used to determine whether a given glyph is an emoji or not when rendering
|
||||||
emojiMask := make([]bool, 0, len(mainBuffer.Info))
|
emojiMask := make([]bool, len(emojiBuffer.Info))
|
||||||
|
|
||||||
|
if len(mainBuffer.Info) > len(emojiBuffer.Info) {
|
||||||
|
// remove from mainBuffer characters that are not present in emojiBuffer
|
||||||
|
newMainBufferInfo := make([]harfbuzz.GlyphInfo, len(emojiBuffer.Info))
|
||||||
|
newMainBufferPos := make([]harfbuzz.GlyphPosition, len(emojiBuffer.Info))
|
||||||
|
for e, m := 0, 0; e < len(emojiBuffer.Info); {
|
||||||
|
ec := emojiBuffer.Info[e].Codepoint
|
||||||
|
if ec == mainBuffer.Info[m].Codepoint {
|
||||||
|
newMainBufferInfo[e] = mainBuffer.Info[m]
|
||||||
|
newMainBufferPos[e] = mainBuffer.Pos[m]
|
||||||
|
|
||||||
|
if emoji.IsEmoji(ec) || emoji.IsTag(ec) || emoji.IsRegionalIndicator(ec) {
|
||||||
|
emojiMask[e] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
e++
|
||||||
|
m++
|
||||||
|
} else {
|
||||||
|
m++
|
||||||
|
for ; emojiBuffer.Info[e].Codepoint != mainBuffer.Info[m].Codepoint; m++ {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mainBuffer.Info = newMainBufferInfo
|
||||||
|
mainBuffer.Pos = newMainBufferPos
|
||||||
|
} else {
|
||||||
|
// just go through the glyphs and decide which ones are emojis
|
||||||
|
for e := range emojiBuffer.Info {
|
||||||
|
ec := emojiBuffer.Info[e].Codepoint
|
||||||
|
if emoji.IsEmoji(ec) || emoji.IsTag(ec) || emoji.IsRegionalIndicator(ec) {
|
||||||
|
emojiMask[e] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert the shaped text into an output
|
// convert the shaped text into an output
|
||||||
glyphs := make([]shaping.Glyph, 0, len(mainBuffer.Info))
|
glyphs := make([]shaping.Glyph, len(mainBuffer.Info))
|
||||||
g := -1
|
for i := 0; i < len(glyphs); i++ {
|
||||||
fmt.Printf("[::] %s %X\n", string(rawText), rawText)
|
|
||||||
for i := 0; i < len(mainBuffer.Info); i++ {
|
|
||||||
g++
|
|
||||||
var buf *harfbuzz.Buffer
|
var buf *harfbuzz.Buffer
|
||||||
var font *harfbuzz.Font
|
var font *harfbuzz.Font
|
||||||
if chars, visChars := emoji.GetNextEmojiCharacters(rawText[i:], len(rawText)-i); chars > 0 {
|
if emojiMask[i] {
|
||||||
buf = emojiBuffer
|
buf = emojiBuffer
|
||||||
font = emojiFont
|
font = emojiFont
|
||||||
emojiMask = append(emojiMask, true)
|
|
||||||
|
|
||||||
// remove the invalid glyphs from mainBuffer
|
|
||||||
fmt.Println(chars, visChars)
|
|
||||||
if chars > 1 {
|
|
||||||
cutN := chars - 1
|
|
||||||
|
|
||||||
for _, g := range mainBuffer.Info[g+1 : len(mainBuffer.Info[g+1+cutN:])] {
|
|
||||||
fmt.Printf(" excluding %s %X \n", string(g.Codepoint), g.Codepoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(mainBuffer.Info[g+1:], mainBuffer.Info[g+1+cutN:])
|
|
||||||
mainBuffer.Info = mainBuffer.Info[0 : len(mainBuffer.Info)-cutN]
|
|
||||||
copy(mainBuffer.Pos[g+1:], mainBuffer.Pos[g+1+cutN:])
|
|
||||||
mainBuffer.Pos = mainBuffer.Pos[0 : len(mainBuffer.Pos)-cutN]
|
|
||||||
i += chars - 1
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
emojiMask = append(emojiMask, false)
|
|
||||||
buf = mainBuffer
|
buf = mainBuffer
|
||||||
font = mainFont
|
font = mainFont
|
||||||
}
|
}
|
||||||
|
glyph := buf.Info[i]
|
||||||
|
|
||||||
glyph := buf.Info[g]
|
glyphs[i] = shaping.Glyph{
|
||||||
glyphs = append(glyphs, shaping.Glyph{
|
|
||||||
ClusterIndex: glyph.Cluster,
|
ClusterIndex: glyph.Cluster,
|
||||||
GlyphID: glyph.Glyph,
|
GlyphID: glyph.Glyph,
|
||||||
Mask: glyph.Mask,
|
Mask: glyph.Mask,
|
||||||
})
|
}
|
||||||
extents, ok := font.GlyphExtents(glyph.Glyph)
|
extents, ok := font.GlyphExtents(glyph.Glyph)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
idx := len(glyphs) - 1
|
glyphs[i].Width = fixed.I(int(extents.Width)) >> scaleShift
|
||||||
glyphs[idx].Width = fixed.I(int(extents.Width)) >> scaleShift
|
glyphs[i].Height = fixed.I(int(extents.Height)) >> scaleShift
|
||||||
glyphs[idx].Height = fixed.I(int(extents.Height)) >> scaleShift
|
glyphs[i].XBearing = fixed.I(int(extents.XBearing)) >> scaleShift
|
||||||
glyphs[idx].XBearing = fixed.I(int(extents.XBearing)) >> scaleShift
|
glyphs[i].YBearing = fixed.I(int(extents.YBearing)) >> scaleShift
|
||||||
glyphs[idx].YBearing = fixed.I(int(extents.YBearing)) >> scaleShift
|
glyphs[i].XAdvance = fixed.I(int(buf.Pos[i].XAdvance)) >> scaleShift
|
||||||
glyphs[idx].XAdvance = fixed.I(int(buf.Pos[g].XAdvance)) >> scaleShift
|
glyphs[i].YAdvance = fixed.I(int(buf.Pos[i].YAdvance)) >> scaleShift
|
||||||
glyphs[idx].YAdvance = fixed.I(int(buf.Pos[g].YAdvance)) >> scaleShift
|
glyphs[i].XOffset = fixed.I(int(buf.Pos[i].XOffset)) >> scaleShift
|
||||||
glyphs[idx].XOffset = fixed.I(int(buf.Pos[g].XOffset)) >> scaleShift
|
glyphs[i].YOffset = fixed.I(int(buf.Pos[i].YOffset)) >> scaleShift
|
||||||
glyphs[idx].YOffset = fixed.I(int(buf.Pos[g].YOffset)) >> scaleShift
|
|
||||||
}
|
}
|
||||||
|
|
||||||
countClusters(glyphs, input.RunEnd, input.Direction.Progression())
|
countClusters(glyphs, input.RunEnd, input.Direction.Progression())
|
||||||
@@ -492,6 +503,46 @@ func shapeText(rawText []rune, fontSize int) (shaping.Output, []bool) {
|
|||||||
return out, emojiMask
|
return out, emojiMask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this function is copied from go-text/typesetting/shaping because shapeText needs it
|
||||||
|
func countClusters(glyphs []shaping.Glyph, textLen int, dir di.Progression) {
|
||||||
|
currentCluster := -1
|
||||||
|
runesInCluster := 0
|
||||||
|
glyphsInCluster := 0
|
||||||
|
previousCluster := textLen
|
||||||
|
for i := range glyphs {
|
||||||
|
g := glyphs[i].ClusterIndex
|
||||||
|
if g != currentCluster {
|
||||||
|
// If we're processing a new cluster, count the runes and glyphs
|
||||||
|
// that compose it.
|
||||||
|
runesInCluster = 0
|
||||||
|
glyphsInCluster = 1
|
||||||
|
currentCluster = g
|
||||||
|
nextCluster := -1
|
||||||
|
glyphCountLoop:
|
||||||
|
for k := i + 1; k < len(glyphs); k++ {
|
||||||
|
if glyphs[k].ClusterIndex == g {
|
||||||
|
glyphsInCluster++
|
||||||
|
} else {
|
||||||
|
nextCluster = glyphs[k].ClusterIndex
|
||||||
|
break glyphCountLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nextCluster == -1 {
|
||||||
|
nextCluster = textLen
|
||||||
|
}
|
||||||
|
switch dir {
|
||||||
|
case di.FromTopLeft:
|
||||||
|
runesInCluster = nextCluster - currentCluster
|
||||||
|
case di.TowardTopLeft:
|
||||||
|
runesInCluster = previousCluster - currentCluster
|
||||||
|
}
|
||||||
|
previousCluster = g
|
||||||
|
}
|
||||||
|
glyphs[i].GlyphCount = glyphsInCluster
|
||||||
|
glyphs[i].RuneCount = runesInCluster
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// this function is copied from go-text/render, but adapted to not require a "class" to be instantiated and also,
|
// this function is copied from go-text/render, but adapted to not require a "class" to be instantiated and also,
|
||||||
// more importantly, to take an emojiMask parameter, with the same length as out.Glyphs, to determine when a
|
// more importantly, to take an emojiMask parameter, with the same length as out.Glyphs, to determine when a
|
||||||
// glyph should be rendered with the emoji font instead of with the default font
|
// glyph should be rendered with the emoji font instead of with the default font
|
||||||
@@ -562,46 +613,6 @@ func drawOutline(g shaping.Glyph, bitmap api.GlyphOutline, f *rasterx.Filler, sc
|
|||||||
f.Stop(true)
|
f.Stop(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this function is copied from go-text/typesetting/shaping because shapeText needs it
|
|
||||||
func countClusters(glyphs []shaping.Glyph, textLen int, dir di.Progression) {
|
|
||||||
currentCluster := -1
|
|
||||||
runesInCluster := 0
|
|
||||||
glyphsInCluster := 0
|
|
||||||
previousCluster := textLen
|
|
||||||
for i := range glyphs {
|
|
||||||
g := glyphs[i].ClusterIndex
|
|
||||||
if g != currentCluster {
|
|
||||||
// If we're processing a new cluster, count the runes and glyphs
|
|
||||||
// that compose it.
|
|
||||||
runesInCluster = 0
|
|
||||||
glyphsInCluster = 1
|
|
||||||
currentCluster = g
|
|
||||||
nextCluster := -1
|
|
||||||
glyphCountLoop:
|
|
||||||
for k := i + 1; k < len(glyphs); k++ {
|
|
||||||
if glyphs[k].ClusterIndex == g {
|
|
||||||
glyphsInCluster++
|
|
||||||
} else {
|
|
||||||
nextCluster = glyphs[k].ClusterIndex
|
|
||||||
break glyphCountLoop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nextCluster == -1 {
|
|
||||||
nextCluster = textLen
|
|
||||||
}
|
|
||||||
switch dir {
|
|
||||||
case di.FromTopLeft:
|
|
||||||
runesInCluster = nextCluster - currentCluster
|
|
||||||
case di.TowardTopLeft:
|
|
||||||
runesInCluster = previousCluster - currentCluster
|
|
||||||
}
|
|
||||||
previousCluster = g
|
|
||||||
}
|
|
||||||
glyphs[i].GlyphCount = glyphsInCluster
|
|
||||||
glyphs[i].RuneCount = runesInCluster
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fixed266ToFloat(i fixed.Int26_6) float32 {
|
func fixed266ToFloat(i fixed.Int26_6) float32 {
|
||||||
return float32(float64(i) / 64)
|
return float32(float64(i) / 64)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user