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)
+ }
}
}