Skip to content

Commit

Permalink
Works for inline svg
Browse files Browse the repository at this point in the history
  • Loading branch information
thomiceli committed Oct 13, 2024
1 parent 6959929 commit 89d2d5e
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 63 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.23.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,8 @@ golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
Expand Down
65 changes: 65 additions & 0 deletions internal/render/checkbox.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package render

import (
"bufio"
"bytes"
"github.com/Kunde21/markdownfmt/v3"
"github.com/rs/zerolog/log"
"github.com/yuin/goldmark/ast"
astex "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"strconv"
)

type checkboxTransformer struct{}

func (t *checkboxTransformer) Transform(node *ast.Document, _ text.Reader, _ parser.Context) {
i := 1
err := ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if _, ok := n.(*astex.TaskCheckBox); ok {
listitem := n.Parent().Parent()
listitem.SetAttribute([]byte("data-checkbox-nb"), []byte(strconv.Itoa(i)))
i += 1
}
}
return ast.WalkContinue, nil
})
if err != nil {
log.Err(err)
}
}

func Checkbox(content string, checkboxNb int) (string, error) {
buf := bytes.Buffer{}
w := bufio.NewWriter(&buf)

source := []byte(content)
markdown := markdownfmt.NewGoldmark()
reader := text.NewReader(source)
document := markdown.Parser().Parse(reader)

i := 1
err := ast.Walk(document, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if listItem, ok := n.(*astex.TaskCheckBox); ok {
if i == checkboxNb {
listItem.IsChecked = !listItem.IsChecked
}
i += 1
}
}
return ast.WalkContinue, nil
})
if err != nil {
return "", err
}

if err = markdown.Renderer().Render(w, source, document); err != nil {
return "", err
}
_ = w.Flush()

return buf.String(), nil
}
62 changes: 2 additions & 60 deletions internal/render/markdown.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package render

import (
"bufio"
"bytes"
"github.com/Kunde21/markdownfmt/v3"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"github.com/yuin/goldmark"
emoji "github.com/yuin/goldmark-emoji"
highlighting "github.com/yuin/goldmark-highlighting/v2"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension"
astex "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
"go.abhg.dev/goldmark/mermaid"
"strconv"
)

func MarkdownGistPreview(gist *db.Gist) (RenderedGist, error) {
Expand Down Expand Up @@ -57,63 +50,12 @@ func newMarkdown() goldmark.Markdown {
highlighting.WithFormatOptions(html.WithClasses(true))),
emoji.Emoji,
&mermaid.Extender{},
&svgToImg{},
),
goldmark.WithParserOptions(
parser.WithASTTransformers(
util.Prioritized(&CheckboxTransformer{}, 10000),
util.Prioritized(&checkboxTransformer{}, 10000),
),
),
)
}

type CheckboxTransformer struct{}

func (t *CheckboxTransformer) Transform(node *ast.Document, _ text.Reader, _ parser.Context) {
i := 1
err := ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if _, ok := n.(*astex.TaskCheckBox); ok {
listitem := n.Parent().Parent()
listitem.SetAttribute([]byte("data-checkbox-nb"), []byte(strconv.Itoa(i)))
i += 1
}
}
return ast.WalkContinue, nil
})
if err != nil {
log.Err(err)
}
}

func Checkbox(content string, checkboxNb int) (string, error) {
buf := bytes.Buffer{}
w := bufio.NewWriter(&buf)

source := []byte(content)
markdown := markdownfmt.NewGoldmark()
reader := text.NewReader(source)
document := markdown.Parser().Parse(reader)

i := 1
err := ast.Walk(document, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
if entering {
if listItem, ok := n.(*astex.TaskCheckBox); ok {
if i == checkboxNb {
listItem.IsChecked = !listItem.IsChecked
}
i += 1
}
}
return ast.WalkContinue, nil
})
if err != nil {
return "", err
}

if err = markdown.Renderer().Render(w, source, document); err != nil {
return "", err
}
_ = w.Flush()

return buf.String(), nil
}
73 changes: 73 additions & 0 deletions internal/render/svgtoimg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package render

import (
"bytes"
"encoding/base64"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)

type svgToImg struct{}

func (e *svgToImg) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(newInlineSvgParser(), 1),
))
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(newInlineSvgRenderer(), 1),
))
}

type inlineSvgParser struct{}

func newInlineSvgParser() parser.InlineParser {
return &inlineSvgParser{}
}

func (p *inlineSvgParser) Trigger() []byte {
return []byte{'<'}
}

func (p *inlineSvgParser) Parse(_ ast.Node, block text.Reader, pc parser.Context) ast.Node {
line, _ := block.PeekLine()
if bytes.HasPrefix(line, []byte("<svg")) {
node := ast.NewRawHTML()
_, savedSegment := block.Position()
node.Segments.Append(text.NewSegment(savedSegment.Start, savedSegment.Start+len(line)))
block.Advance(len(line))
return node
}
return nil
}

func (p *inlineSvgParser) CloseBlock() {}

type inlineSvgRenderer struct{}

func newInlineSvgRenderer() renderer.NodeRenderer {
return &inlineSvgRenderer{}
}

func (r *inlineSvgRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindRawHTML, r.renderSVG)
}

func (r *inlineSvgRenderer) renderSVG(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
rawHTML := node.(*ast.RawHTML)
var svgContent []byte
for i := 0; i < rawHTML.Segments.Len(); i++ {
segment := rawHTML.Segments.At(i)
svgContent = append(svgContent, segment.Value(source)...)
}
encoded := base64.StdEncoding.EncodeToString(svgContent)
imgTag := `<img src="data:image/svg+xml;base64,` + encoded + `" />`
_, _ = w.Write([]byte(imgTag))
return ast.WalkContinue, nil
}

0 comments on commit 89d2d5e

Please sign in to comment.