-
Notifications
You must be signed in to change notification settings - Fork 15
/
renderer.go
122 lines (103 loc) · 3.05 KB
/
renderer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package pdf
import (
"fmt"
"io"
"sync"
"github.com/yuin/goldmark/ast"
goldrender "github.com/yuin/goldmark/renderer"
)
// NewRreturns a new PDF Renderer with given options.
func New(options ...Option) goldrender.Renderer {
config := DefaultConfig()
for _, opt := range options {
opt.SetConfig(config)
}
config.AddDefaultNodeRenderers()
r := &renderer{
config: config,
nodeRendererFuncsTmp: map[ast.NodeKind]NodeRendererFunc{},
}
return r
}
type renderer struct {
config *Config
nodeRendererFuncsTmp map[ast.NodeKind]NodeRendererFunc
maxKind int
initSync sync.Once
nodeRendererFuncs []NodeRendererFunc
}
// AddOptions has no effect on this renderer
// The method is to satisfy goldmark's Renderer interface
func (r *renderer) AddOptions(_ ...goldrender.Option) {
// Nothing to add
}
// Satisfies the NodeRendererFuncRegisterer interface
// used to add NodeRenderers
func (r *renderer) Register(kind ast.NodeKind, v NodeRendererFunc) {
r.nodeRendererFuncsTmp[kind] = v
if int(kind) > r.maxKind {
r.maxKind = int(kind)
}
}
// Render renders the given AST node to the given writer.
func (r *renderer) Render(w io.Writer, source []byte, n ast.Node) error {
r.initSync.Do(func() {
// r.options = r.config.Options
r.config.NodeRenderers.Sort()
l := len(r.config.NodeRenderers)
for i := l - 1; i >= 0; i-- {
v := r.config.NodeRenderers[i]
nr, _ := v.Value.(NodeRenderer)
nr.RegisterFuncs(r)
}
r.nodeRendererFuncs = make([]NodeRendererFunc, r.maxKind+1)
for kind, nr := range r.nodeRendererFuncsTmp {
r.nodeRendererFuncs[kind] = nr
}
})
pdf := r.config.PDF
if pdf == nil {
pdf = NewFpdf(r.config.Context, FpdfConfig{}, nil)
}
// create an empty interna link to the top of the page
pdf.AddInternalLink("")
err := addStyleFonts(r.config.Context, pdf, r.config.Styles, r.config.FontsCache)
if err != nil {
return fmt.Errorf("could not load fonts: %w", err)
}
writer := &Writer{
Pdf: pdf,
ImageFS: mergeFs(r.config.ImageFS, &inlineFs{}, &webFs{}),
Styles: r.config.Styles,
DebugWriter: r.config.TraceWriter,
States: states{stack: make([]*state, 0)},
}
mleft, _, _, _ := pdf.GetMargins()
initcurrent := &state{
containerType: ast.KindParagraph,
listkind: notlist,
textStyle: *r.config.Styles.Normal, leftMargin: mleft,
}
writer.States.push(initcurrent)
err = ast.Walk(n, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
s := ast.WalkStatus(ast.WalkContinue)
var err error
f := r.nodeRendererFuncs[n.Kind()]
if f != nil {
s, err = f(writer, source, n, entering)
}
return s, err
})
if err != nil {
return err
}
return pdf.Write(w)
}
func SetStyle(pdf PDF, s Style) {
textR, textG, textB, _ := s.TextColor.RGBA()
fillR, fillG, fillB, _ := s.FillColor.RGBA()
pdf.SetFont(s.Font.Family, s.format, int(s.Size))
pdf.SetTextColor(uint8(textR>>8), uint8(textG>>8), uint8(textB>>8))
pdf.SetFillColor(uint8(fillR>>8), uint8(fillG>>8), uint8(fillB>>8))
pdf.SetDrawColor(uint8(fillR>>8), uint8(fillG>>8), uint8(fillB>>8))
}