142 lines
2.9 KiB
Go
142 lines
2.9 KiB
Go
|
|
package libgofunc
|
||
|
|
|
||
|
|
import (
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/fogleman/gg"
|
||
|
|
"golang.org/x/image/font"
|
||
|
|
"golang.org/x/net/html"
|
||
|
|
)
|
||
|
|
|
||
|
|
// -------------------- STYLE --------------------
|
||
|
|
type Style struct {
|
||
|
|
PaddingLeft float64
|
||
|
|
PaddingRight float64
|
||
|
|
PaddingTop float64
|
||
|
|
PaddingBottom float64
|
||
|
|
FontSize float64
|
||
|
|
Width float64
|
||
|
|
Height float64
|
||
|
|
TextAlign string
|
||
|
|
VerticalAlign string
|
||
|
|
Border float64
|
||
|
|
}
|
||
|
|
|
||
|
|
func parseStyle(styleStr string) Style {
|
||
|
|
style := Style{
|
||
|
|
FontSize: 16,
|
||
|
|
TextAlign: "left",
|
||
|
|
VerticalAlign: "top",
|
||
|
|
}
|
||
|
|
|
||
|
|
parts := strings.Split(styleStr, ";")
|
||
|
|
for _, p := range parts {
|
||
|
|
kv := strings.SplitN(strings.TrimSpace(p), ":", 2)
|
||
|
|
if len(kv) != 2 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
key := strings.TrimSpace(kv[0])
|
||
|
|
val := strings.TrimSpace(kv[1])
|
||
|
|
|
||
|
|
switch key {
|
||
|
|
case "padding":
|
||
|
|
v := parsePx(val)
|
||
|
|
style.PaddingLeft, style.PaddingRight, style.PaddingTop, style.PaddingBottom = v, v, v, v
|
||
|
|
case "padding-left":
|
||
|
|
v := parsePx(val)
|
||
|
|
style.PaddingLeft = v
|
||
|
|
case "padding-right":
|
||
|
|
v := parsePx(val)
|
||
|
|
style.PaddingRight = v
|
||
|
|
case "padding-top":
|
||
|
|
v := parsePx(val)
|
||
|
|
style.PaddingTop = v
|
||
|
|
case "padding-bottom":
|
||
|
|
v := parsePx(val)
|
||
|
|
style.PaddingBottom = v
|
||
|
|
case "font-size":
|
||
|
|
style.FontSize = parsePx(val)
|
||
|
|
case "width":
|
||
|
|
style.Width = parsePx(val)
|
||
|
|
case "height":
|
||
|
|
style.Height = parsePx(val)
|
||
|
|
case "text-align":
|
||
|
|
style.TextAlign = val
|
||
|
|
case "vertical-align":
|
||
|
|
style.VerticalAlign = val
|
||
|
|
case "border":
|
||
|
|
style.Border = parsePx(val)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return style
|
||
|
|
}
|
||
|
|
|
||
|
|
func parsePx(v string) float64 {
|
||
|
|
v = strings.ReplaceAll(v, "px", "")
|
||
|
|
f, _ := strconv.ParseFloat(v, 64)
|
||
|
|
return f
|
||
|
|
}
|
||
|
|
|
||
|
|
// -------------------- NODE --------------------
|
||
|
|
type Node struct {
|
||
|
|
Tag string
|
||
|
|
Text string
|
||
|
|
Attr map[string]string
|
||
|
|
Style Style
|
||
|
|
Children []*Node
|
||
|
|
}
|
||
|
|
|
||
|
|
func (n Node) getSrc() string {
|
||
|
|
for k, v := range n.Attr {
|
||
|
|
if k == "src" {
|
||
|
|
return v
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return ""
|
||
|
|
}
|
||
|
|
|
||
|
|
func (n Node) getHeight() int {
|
||
|
|
return int(n.Style.Height)
|
||
|
|
}
|
||
|
|
|
||
|
|
func BuildTree(n *html.Node, canvasWidth int, dc *gg.Context, face *font.Face) (*Node, int) {
|
||
|
|
if n.Type != html.ElementNode {
|
||
|
|
return nil, 0
|
||
|
|
}
|
||
|
|
|
||
|
|
text := ""
|
||
|
|
if n.Data == "h1" || n.Data == "h2" || n.Data == "h3" ||
|
||
|
|
n.Data == "p" || n.Data == "td" || n.Data == "th" ||
|
||
|
|
n.Type == html.TextNode {
|
||
|
|
text = getText(n)
|
||
|
|
}
|
||
|
|
|
||
|
|
node := &Node{
|
||
|
|
Tag: n.Data,
|
||
|
|
Text: text,
|
||
|
|
Attr: map[string]string{},
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, a := range n.Attr {
|
||
|
|
node.Attr[a.Key] = a.Val
|
||
|
|
}
|
||
|
|
|
||
|
|
if s, ok := node.Attr["style"]; ok {
|
||
|
|
node.Style = parseStyle(s)
|
||
|
|
}
|
||
|
|
xPadding := node.Style.PaddingLeft + node.Style.PaddingRight
|
||
|
|
yPadding := node.Style.PaddingTop + node.Style.PaddingBottom
|
||
|
|
fontSize := node.Style.FontSize
|
||
|
|
h := nodeHeight(n, dc, canvasWidth, int(xPadding), int(yPadding),
|
||
|
|
face, fontSize, node.getHeight())
|
||
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||
|
|
child, ch := BuildTree(c, canvasWidth, dc, face)
|
||
|
|
if child != nil {
|
||
|
|
node.Children = append(node.Children, child)
|
||
|
|
}
|
||
|
|
h += ch
|
||
|
|
}
|
||
|
|
return node, h
|
||
|
|
}
|