mirror of
https://github.com/thomiceli/opengist.git
synced 2025-07-09 01:18:04 +02:00
Add SVG parser (#346)
This commit is contained in:
141
internal/render/markdown_svg.go
Normal file
141
internal/render/markdown_svg.go
Normal file
@ -0,0 +1,141 @@
|
||||
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"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var svgRegex = regexp.MustCompile(`(?i)^[ ]{0,3}<(svg)(?:\s.*|>.*|/>.*|)(?:\r\n|\n)?$`)
|
||||
|
||||
type svgToImgBase64 struct{}
|
||||
|
||||
func (e *svgToImgBase64) Extend(m goldmark.Markdown) {
|
||||
m.Parser().AddOptions(parser.WithBlockParsers(
|
||||
util.Prioritized(newSvgParser(), 1),
|
||||
))
|
||||
m.Renderer().AddOptions(renderer.WithNodeRenderers(
|
||||
util.Prioritized(newSvgRenderer(), 1),
|
||||
))
|
||||
}
|
||||
|
||||
// -- SVG Block -- //
|
||||
|
||||
type svgBlock struct {
|
||||
ast.BaseBlock
|
||||
}
|
||||
|
||||
func (n *svgBlock) IsRaw() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *svgBlock) Dump(source []byte, level int) {
|
||||
ast.DumpHelper(n, source, level, nil, nil)
|
||||
}
|
||||
|
||||
var svgBlockKind = ast.NewNodeKind("SVG")
|
||||
|
||||
func (n *svgBlock) Kind() ast.NodeKind {
|
||||
return svgBlockKind
|
||||
}
|
||||
|
||||
func newSvgBlock() *svgBlock {
|
||||
return &svgBlock{
|
||||
BaseBlock: ast.BaseBlock{},
|
||||
}
|
||||
}
|
||||
|
||||
// -- SVG Parser -- //
|
||||
|
||||
type svgParser struct {
|
||||
}
|
||||
|
||||
var defaultSvgParser = &svgParser{}
|
||||
|
||||
func newSvgParser() parser.BlockParser {
|
||||
return defaultSvgParser
|
||||
}
|
||||
|
||||
func (b *svgParser) Trigger() []byte {
|
||||
return []byte{'<'}
|
||||
}
|
||||
|
||||
func (b *svgParser) Open(parent ast.Node, reader text.Reader, _ parser.Context) (ast.Node, parser.State) {
|
||||
var node *svgBlock
|
||||
line, segment := reader.PeekLine()
|
||||
|
||||
if !bytes.HasPrefix(line, []byte("<svg")) {
|
||||
return nil, parser.None
|
||||
}
|
||||
|
||||
if svgRegex.Match(line) {
|
||||
node = newSvgBlock()
|
||||
}
|
||||
|
||||
if node != nil {
|
||||
reader.Advance(segment.Len() - util.TrimRightSpaceLength(line))
|
||||
node.Lines().Append(segment)
|
||||
return node, parser.NoChildren
|
||||
}
|
||||
return nil, parser.None
|
||||
}
|
||||
|
||||
func (b *svgParser) Continue(node ast.Node, reader text.Reader, _ parser.Context) parser.State {
|
||||
line, segment := reader.PeekLine()
|
||||
if util.IsBlank(line) {
|
||||
return parser.Close
|
||||
}
|
||||
|
||||
if !bytes.HasSuffix(util.TrimRightSpace(line), []byte("</svg>")) {
|
||||
node.Lines().Append(segment)
|
||||
return parser.Continue | parser.NoChildren
|
||||
}
|
||||
|
||||
node.Lines().Append(segment)
|
||||
reader.Advance(segment.Len())
|
||||
return parser.Close
|
||||
}
|
||||
|
||||
func (b *svgParser) Close(_ ast.Node, _ text.Reader, _ parser.Context) {}
|
||||
|
||||
func (b *svgParser) CanInterruptParagraph() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *svgParser) CanAcceptIndentedLine() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// -- SVG Renderer -- //
|
||||
|
||||
type svgRenderer struct{}
|
||||
|
||||
func newSvgRenderer() renderer.NodeRenderer {
|
||||
return &svgRenderer{}
|
||||
}
|
||||
|
||||
func (r *svgRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
reg.Register(svgBlockKind, r.renderSVG)
|
||||
}
|
||||
|
||||
func (r *svgRenderer) renderSVG(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
if !entering {
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
rawHTML := node.(*svgBlock)
|
||||
var svgContent []byte
|
||||
for i := 0; i < rawHTML.Lines().Len(); i++ {
|
||||
segment := rawHTML.Lines().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
|
||||
}
|
Reference in New Issue
Block a user