shithub: mycel

Download patch

ref: b61213d33be35446f1fff9efcb83adbb3eaf4d54
parent: 35e034e341b0794b327eabb3f25392e3b27dd99b
author: Philip Silva <philip.silva@protonmail.com>
date: Sun Sep 19 09:24:07 EDT 2021

initial css var support

--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@
 consider turning on scroll since processing
 waits for that...)
 
-`$font` is used to select the font.
+`$font` is used to select the font. Very large fonts will set dpi to 200.
 
 ## macOS
 
--- a/style/fonts_plan9.go
+++ b/style/fonts_plan9.go
@@ -18,6 +18,10 @@
 )
 
 func initFontserver() {
+	if dui == nil {
+		// unit test
+		return
+	}
 	if df := dui.Font(nil); df.Height >= 40 {
 		dui.Display.DPI = 200
 	}
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -110,7 +110,7 @@
 }
 
 func FetchNodeMap(doc *html.Node, cssText string, windowWidth int) (m map[*html.Node]Map, err error) {
-	mr, err := FetchNodeRules(doc, cssText, windowWidth)
+	mr, rv, err := FetchNodeRules(doc, cssText, windowWidth)
 	if err != nil {
 		return nil, fmt.Errorf("fetch rules: %w", err)
 	}
@@ -122,6 +122,13 @@
 				if exist, ok := ds[d.Property]; ok && smaller(*d, exist) {
 					continue
 				}
+				if strings.HasPrefix(d.Value, "var(") {
+					v := strings.TrimPrefix(d.Value, "var(")
+					v = strings.TrimSuffix(v, ")")
+					if vv, ok := rv[v]; ok {
+						d.Value = vv
+					}
+				}
 				ds[d.Property] = *d
 			}
 		}
@@ -147,14 +154,20 @@
 	return cascadia.Compile(v)
 }
 
-func FetchNodeRules(doc *html.Node, cssText string, windowWidth int) (m map[*html.Node][]*css.Rule, err error) {
+func FetchNodeRules(doc *html.Node, cssText string, windowWidth int) (m map[*html.Node][]*css.Rule, rVars map[string]string, err error) {
 	m = make(map[*html.Node][]*css.Rule)
+	rVars = make(map[string]string)
 	s, err := parser.Parse(cssText)
 	if err != nil {
-		return nil, fmt.Errorf("douceur parse: %w", err)
+		return nil, nil, fmt.Errorf("douceur parse: %w", err)
 	}
 	processRule := func(m map[*html.Node][]*css.Rule, r *css.Rule) (err error) {
 		for _, sel := range r.Selectors {
+			if sel.Value == ":root" {
+				for _, d := range r.Declarations {
+					rVars[d.Property] = d.Value
+				}
+			}
 			cs, err := compile(sel.Value)
 			if err != nil {
 				log.Printf("cssSel compile %v: %v", sel.Value, err)
@@ -173,7 +186,7 @@
 	}
 	for _, r := range s.Rules {
 		if err := processRule(m, r); err != nil {
-			return nil, fmt.Errorf("process rule: %w", err)
+			return nil, nil, fmt.Errorf("process rule: %w", err)
 		}
 
 		// for media queries
@@ -185,7 +198,7 @@
 			l := m[1]+m[2]
 			maxWidth, _, err := length(nil, l)
 			if err != nil {
-				return nil, fmt.Errorf("atoi: %w", err)
+				return nil, nil, fmt.Errorf("atoi: %w", err)
 			}
 			if float64(windowWidth) > maxWidth {
 				continue
@@ -196,7 +209,7 @@
 			l := m[1]+m[2]
 			minWidth, _, err := length(nil, l)
 			if err != nil {
-				return nil, fmt.Errorf("atoi: %w", err)
+				return nil, nil, fmt.Errorf("atoi: %w", err)
 			}
 			if float64(windowWidth) < minWidth {
 				continue
@@ -204,7 +217,7 @@
 		}
 		for _, rr := range r.Rules {
 			if err := processRule(m, rr); err != nil {
-				return nil, fmt.Errorf("process embedded rule: %w", err)
+				return nil, nil, fmt.Errorf("process embedded rule: %w", err)
 			}
 		}
 	}
--- a/style/stylesheets_test.go
+++ b/style/stylesheets_test.go
@@ -67,7 +67,7 @@
 	`
 	for _, w := range []int{400, 800} {
 		t.Logf("w=%v", w)
-		m, err := FetchNodeRules(doc, css, w)
+		m, _, err := FetchNodeRules(doc, css, w)
 		if err != nil {
 			t.Fail()
 		}
@@ -124,7 +124,7 @@
 	if err != nil {
 		t.Fail()
 	}
-	m, err := FetchNodeRules(doc, AddOnCSS, 1024)
+	m, _, err := FetchNodeRules(doc, AddOnCSS, 1024)
 	if err != nil {
 		t.Fail()
 	}
@@ -354,3 +354,57 @@
 		}
 	}
 }
+
+func TestCssVars(t *testing.T) {
+	data := `<body>
+      		<h2 id="foo">a header</h2>
+     		<h2 id="bar">another header</h2>
+   		<p>Some text <b>in bold</b></p>
+    	</body>`
+	doc, err := html.Parse(strings.NewReader(data))
+	if err != nil {
+		t.Fail()
+	}
+	css := AddOnCSS + `
+:root {
+	--emph: red;
+	--h: 10px;
+}
+
+b {
+	color: var(--emph);
+}
+	`
+	
+	_, rv, err := FetchNodeRules(doc, css, 1280)
+	if err != nil {
+		t.Fail()
+	}
+
+	if len(rv) != 2 || rv["--emph"] != "red" || rv["--h"] != "10px" {
+		t.Fail()
+	}
+
+	var b *html.Node
+	var f func(n *html.Node)
+	f = func(n *html.Node) {
+		if n.Data == "b" {
+			b = n
+		}
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			f(c)
+		}
+	}
+	f(doc)
+
+	nm, err := FetchNodeMap(doc, css, 1280)
+	if err != nil {
+		t.Fail()
+	}
+	d := nm[b]
+	t.Logf("d=%+v", d)
+	if d.Declarations["color"].Value != "red" {
+		t.Fail()
+	}
+}
+