shithub: mycel

Download patch

ref: e3e41088f4011383f89599c15f3b7d4add542b29
parent: 925c60a4e2ecf694943317b43b42a18d74982386
author: Philip Silva <philip.silva@protonmail.com>
date: Sun Jul 25 15:37:49 EDT 2021

support css calc

--- a/README.md
+++ b/README.md
@@ -80,8 +80,6 @@
 
 On 9legacy also the folder `/mnt/opossum` needs to exist.
 
-```
-
 Then it can be tested with:
 
 ```
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -2,6 +2,7 @@
 
 import (
 	"9fans.net/go/draw"
+	"bytes"
 	"fmt"
 	"github.com/andybalholm/cascadia"
 	"github.com/chris-ramon/douceur/css"
@@ -11,6 +12,7 @@
 	"golang.org/x/net/html"
 	"github.com/psilva261/opossum/logger"
 	"math"
+	"os/exec"
 	"regexp"
 	"strconv"
 	"strings"
@@ -561,11 +563,56 @@
 	return
 }
 
+var reBcInput = regexp.MustCompile(`^[0-9\+\*\-\/\(\)\\.\s]+$`)
+
+func calc(cs *Map, l string) (f float64, unit string, err error) {
+	if !strings.HasPrefix(l, "calc(") && !strings.HasSuffix(l, ")") {
+		return 0, "", fmt.Errorf("wrong format")
+	}
+	l = strings.TrimPrefix(l, "calc(")
+	l = strings.TrimSuffix(l, ")")
+	if len(l) > 50 {
+		return 0, "", fmt.Errorf("parse expression: %v", l)
+	}
+	l = strings.ReplaceAll(l, "px", "")
+	for _, u := range []string{"rem", "em", "%", "vw", "vh"} {
+		if !strings.Contains(l, u) {
+			continue
+		}
+		r, _, err := length(cs, "1"+u)
+		if err != nil {
+			return 0, "", fmt.Errorf("u %v: %v", u, err)
+		}
+		l = strings.ReplaceAll(l, u, fmt.Sprintf("*%v", r))
+	}
+	if !reBcInput.MatchString(l) {
+		return 0, "", fmt.Errorf("parse expression: %v", l)
+	}
+	cmd := exec.Command("bc")
+	cmd.Stdin = strings.NewReader(l + "\n")
+	var out bytes.Buffer
+	var er bytes.Buffer
+	cmd.Stdout = &out
+	cmd.Stderr = &er
+	if e := er.String(); e != "" {
+		log.Errorf("bc: %v", e)
+	}
+	if err = cmd.Run(); err != nil {
+		return
+	}
+	f, err = strconv.ParseFloat(strings.TrimSpace(out.String()), 64)
+	return
+}
+
 func length(cs *Map, l string) (f float64, unit string, err error) {
 	var s string
 
-	if l == "auto" || l == "inherit" || l == "0" {
+	if l == "auto" || l == "inherit" || l == "initial" || l == "0" {
 		return 0, "px", nil
+	}
+
+	if strings.Contains(l, "calc") {
+		return calc(cs, l)
 	}
 
 	for _, suffix := range []string{"px", "%", "rem", "em", "vw", "vh"} {
--- a/style/stylesheets_test.go
+++ b/style/stylesheets_test.go
@@ -2,12 +2,17 @@
 
 import (
 	"github.com/chris-ramon/douceur/css"
-	"golang.org/x/net/html"
 	"github.com/mjl-/duit"
+	"github.com/psilva261/opossum/logger"
+	"golang.org/x/net/html"
 	"strings"
 	"testing"
 )
 
+func init() {
+	log.Debug = true
+}
+
 func d(c string) Map {
 	m := Map{
 		Declarations: make(map[string]css.Declaration),
@@ -275,6 +280,42 @@
 	res := parent.ApplyChildStyle(child, true)
 	if v := res.Declarations["font-size"].Value; v != "12pt" {
 		t.Fatalf(v)
+	}
+}
+
+func TestCalc(t *testing.T) {
+	tests := map[string]float64{
+		"calc(1px+2px)": 3.0,
+		"calc(1px + 2px)": 3.0,
+		"calc(1em+2px)": 13.0,
+		"calc(1em+(2px-1px))": 12.0,
+		"calc(1em+(2px-1.5px))": 11.5,
+	}
+	for x, px := range tests {
+		f, _, err := length(nil, x)
+		if err != nil {
+			t.Fatalf("%v: %v", x, err)
+		}
+		if f != px {
+			t.Fatalf("expected %v but got %v", px, f)
+		}		
+	}
+}
+
+func TestCalc2(t *testing.T) {
+	fails := []string{
+		"calc(a+2px)",
+		"calc(if(1)2)",
+		"calc(quit)",
+		"calc(1;)",
+		"calc()",
+		"calc(" + strings.Repeat("1", 51) + ")",
+	}
+	for _, x := range fails {
+		_, _, err := length(nil, x)
+		if err == nil {
+			t.Fatalf("%v: %v", x, err)
+		}
 	}
 }