shithub: mycel

Download patch

ref: ac353fabcd30e7eba106cb11d05ee8e08a749173
parent: 5aba680979d4c267ad5b279810d24c85fdf208b1
author: Philip Silva <philip.silva@protonmail.com>
date: Mon Jul 19 10:29:15 EDT 2021

Improve relative width handling

--- a/browser/browser.go
+++ b/browser/browser.go
@@ -63,14 +63,9 @@
 var dui *duit.DUI
 var colorCache = make(map[draw.Color]*draw.Image)
 var imageCache = make(map[string]*draw.Image)
-var log *logger.Logger
 var scroller *duitx.Scroll
 var display *draw.Display
 
-func SetLogger(l *logger.Logger) {
-	log = l
-}
-
 type Label struct {
 	*duitx.Label
 
@@ -220,7 +215,10 @@
 	}
 
 	if i, cached = imageCache[src]; !cached {
-		r, err := img.Load(browser, src, n.Width(), n.Height())
+		mw, _ := n.CssPx("max-width")
+		w := n.Width()
+		h := n.Height()
+		r, err := img.Load(browser, src, mw, w, h)
 		if err != nil {
 			return nil, fmt.Errorf("load draw image: %w", err)
 		}
--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -18,10 +18,8 @@
 )
 
 func init() {
-	logger.Init()
-	SetLogger(&logger.Logger{})
-	nodes.SetLogger(log)
-	style.Init(nil, log)
+	log.Debug = true
+	style.Init(nil)
 }
 
 type item struct {
@@ -404,5 +402,42 @@
 	src := newPicture(p)
 	if src != "https://example.com/700" {
 		t.Error()
+	}
+}
+
+func TestWidths(t *testing.T) {
+	htm := `
+<html>
+	<body style="width: 100%">
+		<h1>
+			Info
+		</h1>
+		<main style="width: 50%">
+			<nav style="width: 33%">
+			</nav>
+			<article id="lo">
+				<h2>
+					General information
+				</h2>
+				<p style="width: 90%">
+					Supplementary information
+				</p>
+			</article>
+		</main>
+	</body>
+</html>
+	`
+	nt, _, err := digestHtm(htm)
+	if err != nil {
+		t.Fatalf("digest: %v", err)
+	}
+	if nt.Data() != "body" || nt.Width() != 1280 {
+		t.Fail()
+	}
+	if main := nt.Find("main"); main.Width() != 640 {
+		t.Fail()
+	}
+	if p := nt.Find("p"); p.Width() != 576 {
+		t.Fail()
 	}
 }
--- a/browser/duitx/duitx.go
+++ /dev/null
@@ -1,11 +1,0 @@
-package duitx
-
-import (
-	"github.com/psilva261/opossum/logger"
-)
-
-var log *logger.Logger
-
-func SetLogger(l *logger.Logger) {
-	log = l
-}
\ No newline at end of file
--- a/browser/duitx/scroll.go
+++ b/browser/duitx/scroll.go
@@ -27,6 +27,7 @@
 
 	"9fans.net/go/draw"
 	"github.com/mjl-/duit"
+	"github.com/psilva261/opossum/logger"
 )
 
 // Scroll shows a part of its single child, typically a box, and lets you scroll the content.
--- a/browser/experimental.go
+++ b/browser/experimental.go
@@ -5,6 +5,7 @@
 	"image"
 	"github.com/psilva261/opossum/browser/duitx"
 	"github.com/psilva261/opossum/js"
+	"github.com/psilva261/opossum/logger"
 	"9fans.net/go/draw"
 	"github.com/mjl-/duit"
 )
--- a/browser/experimental_test.go
+++ b/browser/experimental_test.go
@@ -2,10 +2,8 @@
 
 import (
 	"golang.org/x/net/html"
-	//"github.com/mjl-/duit"
 	"github.com/psilva261/opossum/browser/fs"
 	"github.com/psilva261/opossum/js"
-	"github.com/psilva261/opossum/logger"
 	"github.com/psilva261/opossum/nodes"
 	"github.com/psilva261/opossum/style"
 	"io/ioutil"
@@ -14,11 +12,7 @@
 )
 
 func init() {
-	js.SetLogger(&logger.Logger{})
-	logger.Init()
-	SetLogger(&logger.Logger{})
-	style.Init(nil, &logger.Logger{})
-	fs.SetLogger(log)
+	style.Init(nil)
 	go fs.Srv9p()
 }
 
--- a/browser/fs/fs.go
+++ b/browser/fs/fs.go
@@ -17,7 +17,6 @@
 )
 
 var (
-	log *logger.Logger
 	mu  *sync.RWMutex
 	c   *sync.Cond
 
@@ -39,10 +38,6 @@
 func init() {
 	mu = &sync.RWMutex{}
 	c = sync.NewCond(mu)
-}
-
-func SetLogger(l *logger.Logger) {
-	log = l
 }
 
 func Srv9p() {
--- a/browser/fs/fs_plan9.go
+++ b/browser/fs/fs_plan9.go
@@ -3,6 +3,7 @@
 import (
 	"fmt"
 	"github.com/knusbaum/go9p"
+	"github.com/psilva261/opossum/logger"
 	"os"
 	"syscall"
 )
--- a/browser/website.go
+++ b/browser/website.go
@@ -9,6 +9,7 @@
 	"github.com/psilva261/opossum/browser/duitx"
 	"github.com/psilva261/opossum/browser/fs"
 	"github.com/psilva261/opossum/js"
+	"github.com/psilva261/opossum/logger"
 	"github.com/psilva261/opossum/nodes"
 	"github.com/psilva261/opossum/style"
 	"strings"
--- a/cmd/gojafs/domino/domino.go
+++ b/cmd/gojafs/domino/domino.go
@@ -23,7 +23,6 @@
 	"time"
 )
 
-var log *logger.Logger
 var timeout = 60*time.Second
 
 //go:embed domino-lib/*js
@@ -40,10 +39,6 @@
 		panic(err.Error())
 	}
 	domIntf = string(data)
-}
-
-func SetLogger(l *logger.Logger) {
-	log = l
 }
 
 type Mutation struct {
--- a/cmd/gojafs/domino/domino_test.go
+++ b/cmd/gojafs/domino/domino_test.go
@@ -20,8 +20,7 @@
 `
 
 func init() {
-	logger.Init()
-	log = &logger.Logger{Debug: true}
+	log.Debug = true
 }
 
 func TestSimple(t *testing.T) {
--- a/cmd/gojafs/main.go
+++ b/cmd/gojafs/main.go
@@ -22,7 +22,6 @@
 
 var (
 	d *domino.Domino
-	log *logger.Logger
 	service string
 	mtpt    string
 	htm     string
@@ -31,10 +30,7 @@
 )
 
 func init() {
-	logger.Quiet = true
-	logger.Init()
-	log = &logger.Logger{Debug: true}
-	domino.SetLogger(log)
+	log.SetQuiet()
 }
 
 func usage() {
--- a/cmd/gojafs/main_plan9.go
+++ b/cmd/gojafs/main_plan9.go
@@ -2,6 +2,7 @@
 
 import (
 	"fmt"
+	"github.com/psilva261/opossum/logger"
 	"io"
 	"os"
 )
--- a/cmd/opossum/main.go
+++ b/cmd/opossum/main.go
@@ -6,14 +6,10 @@
 	"image"
 	"os"
 	"github.com/knusbaum/go9p"
-	"github.com/psilva261/opossum"
 	"github.com/psilva261/opossum/browser"
-	"github.com/psilva261/opossum/browser/fs"
-	"github.com/psilva261/opossum/img"
 	"github.com/psilva261/opossum/js"
 	"github.com/psilva261/opossum/logger"
 	"github.com/psilva261/opossum/style"
-	"github.com/psilva261/opossum/nodes"
 	"os/signal"
 	"runtime/pprof"
 	"time"
@@ -21,7 +17,6 @@
 )
 
 var dui *duit.DUI
-var log *logger.Logger
 
 var cpuprofile string
 var startPage string = "http://9p.io"
@@ -28,7 +23,7 @@
 var dbg bool
 
 func init() {
-	browser.EnableNoScriptTag = false
+	browser.EnableNoScriptTag = true
 }
 
 func mainView(b *browser.Browser) []*duit.Kid {
@@ -127,13 +122,7 @@
 	}
 	dui.Debug = dbg
 
-	style.Init(dui, log)
-	browser.SetLogger(log)
-	fs.SetLogger(log)
-	img.SetLogger(log)
-	js.SetLogger(log)
-	opossum.SetLogger(log)
-	nodes.SetLogger(log)
+	style.Init(dui)
 
 	b := browser.NewBrowser(dui, startPage)
 	b.Download = func(done chan int) chan string {
@@ -167,16 +156,16 @@
 }
 
 func main() {
-	logger.Quiet = true
+	quiet := true
 	args := os.Args[1:]
 	for len(args) > 0 {
 		switch args[0] {
 		case "-vv":
-			logger.Quiet = false
+			quiet = false
 			dbg = true
 			args = args[1:]
 		case "-v":
-			logger.Quiet = false
+			quiet = false
 			args = args[1:]
 		case "-h":
 			usage()
@@ -193,8 +182,11 @@
 			startPage, args = args[0], args[1:]
 		}
 	}
-	logger.Init()
 
+	if quiet {
+		log.SetQuiet()
+	}
+
 	if cpuprofile != "" {
 		f, err := os.Create(cpuprofile)
 		if err != nil {
@@ -208,7 +200,6 @@
 		}()
 	}
 
-	log = logger.Log
 	log.Debug = dbg
 	go9p.Verbose = log.Debug
 
--- a/img/img.go
+++ b/img/img.go
@@ -22,12 +22,6 @@
 
 const SrcZero = "//:0"
 
-var log *logger.Logger
-
-func SetLogger(l *logger.Logger) {
-	log = l
-}
-
 func parseDataUri(addr string) (data []byte, ct opossum.ContentType, err error) {
 	addr = strings.TrimPrefix(addr, "data:")
 	if strings.Contains(addr, "charset=UTF-8") {
@@ -185,7 +179,7 @@
 }
 
 // Load and resize to w and h if != 0
-func Load(f opossum.Fetcher, src string, w, h int) (r io.Reader, err error) {
+func Load(f opossum.Fetcher, src string, maxW, w, h int) (r io.Reader, err error) {
 	var imgUrl *url.URL
 	var data []byte
 	var contentType opossum.ContentType
@@ -208,7 +202,7 @@
 		if err != nil {
 			return nil, fmt.Errorf("svg: %v", err)
 		}
-	} else if w != 0 || h != 0 {
+	} else if maxW != 0 || w != 0 || h != 0 {
 		img, _, err := image.Decode(bytes.NewReader(data))
 		if err != nil {
 			return nil, fmt.Errorf("decode %v: %w", imgUrl, err)
@@ -216,17 +210,25 @@
 
 		dx := img.Bounds().Max.X
 		dy := img.Bounds().Max.Y
+		log.Printf("dx,dy=%v,%v",dx,dy)
+		if w == 0 && h == 0 && 0 < maxW && maxW < dx {
+			w = maxW
+		}
 
 		newX, newY, skip := newSizes(dx, dy, w, h)
 
 		if !skip {
+			log.Printf("resize image to %v x %v", newX, newY)
 			dst := image.NewRGBA(image.Rect(0, 0, newX, newY))
 			draw.NearestNeighbor.Scale(dst, dst.Rect, img, img.Bounds(), draw.Over, nil)
 			buf := bytes.NewBufferString("")
+
 			if err = png.Encode(buf, dst); err != nil {
 				return nil, fmt.Errorf("encode: %w", err)
 			}
 			data = buf.Bytes()
+		} else {
+			log.Printf("skip resizing")
 		}
 	}
 
@@ -234,18 +236,15 @@
 }
 
 func newSizes(oldX, oldY, wantedX, wantedY int) (newX, newY int, skip bool) {
-	if oldX == 0 || oldY == 0 {
+	if oldX == 0 || oldY == 0 || (wantedX == 0 && wantedY == 0) {
 		return oldX, oldY, true
 	}
 	if wantedX == 0 {
 		newX = int(float64(oldX) * float64(wantedY)/float64(oldY))
 		newY = wantedY
-	} else if wantedY == 0 {
-		newX = wantedX
-		newY = int(float64(oldY) * float64(wantedX)/float64(oldX))
 	} else {
 		newX = wantedX
-		newY = wantedY
+		newY = int(float64(oldY) * float64(wantedX)/float64(oldX))
 	}
 
 	if newX > 2000 || newY > 2000 {
--- a/img/img_test.go
+++ b/img/img_test.go
@@ -1,14 +1,17 @@
 package img
 
 import (
+	"bytes"
+	"github.com/psilva261/opossum"
 	"github.com/psilva261/opossum/logger"
+	"image"
+	"image/png"
+	"net/url"
 	"testing"
 )
 
 func init() {
-	logger.Init()
-	log = &logger.Logger{Debug: true}
-	SetLogger(&logger.Logger{})
+	log.Debug = true
 }
 
 func TestParseDataUri(t *testing.T) {
@@ -78,3 +81,68 @@
 	}
 }
 
+type MockBrowser struct {
+	data []byte
+}
+
+func (b *MockBrowser) Origin() *url.URL { return nil }
+
+func (b *MockBrowser) LinkedUrl(string) (*url.URL, error) { return nil, nil }
+
+func (b *MockBrowser) Get(*url.URL) ([]byte, opossum.ContentType, error) {
+	return b.data, opossum.ContentType{}, nil
+}
+
+func TestLoad(t *testing.T) {
+	rows := [][]int{
+		{1700, 0, 0, 1600, 900},
+		{160, 0, 0, 160, 90},
+		{0, 0, 45, 80, 45},
+		{0, 0, 0, 1600, 900},
+		{0, 800, 800, 800, 450},
+	}
+	for _, r := range rows {
+		t.Logf("test case %+v", r)
+		mw, w, h, xNew, yNew := r[0], r[1], r[2], r[3], r[4]
+		dst := image.NewRGBA(image.Rect(0, 0, 1600, 900))
+		buf := bytes.NewBufferString("")
+		if err := png.Encode(buf, dst); err != nil {
+			t.Fail()
+		}
+		b := &MockBrowser{buf.Bytes()}
+		r, err := Load(b, "", mw, w, h)
+		if err != nil {
+			t.Errorf("load: %v", err)
+		}
+		img, _, err := image.Decode(r)
+		if err != nil {
+			t.Errorf("decode")
+		}
+		dx := img.Bounds().Max.X
+		dy := img.Bounds().Max.Y
+		if dx != xNew || dy != yNew {
+			t.Errorf("unexpected size %v x %v", dx, dy)
+		}
+	}
+}
+
+func TestNewSizes(t *testing.T) {
+	x0 := 400
+	y0 := 300
+
+	x1, y1, _ := newSizes(x0, y0, 100, 0)
+	if x1 != 100 || y1 != 75 {
+		t.Fail()
+	}
+
+	x1, y1, _ = newSizes(x0, y0, 0, 100)
+	if x1 != 133 || y1 != 100 {
+		t.Fail()
+	}
+
+	// Enforce aspect ratio based on width
+	x1, y1, _ = newSizes(x0, y0, 800, 800)
+	if x1 != 800 || y1 != 600 {
+		t.Fail()
+	}
+}
--- a/js/js.go
+++ b/js/js.go
@@ -19,13 +19,8 @@
 	"time"
 )
 
-var log *logger.Logger
 var timeout = 60*time.Second
 
-func SetLogger(l *logger.Logger) {
-	log = l
-}
-
 type ReadWriteCloser struct {
 	io.Reader
 	io.Writer
@@ -128,19 +123,12 @@
 }
 
 func Stop() {
-	log.Infof("Stop devjs")
+	log.Infof("Stop gojafs")
 	if cancel != nil {
 		cancel()
 	}
 }
 
-func printCode(code string, maxWidth int) {
-	if maxWidth > len(code) {
-		maxWidth = len(code)
-	}
-	log.Infof("js code: %v", code[:maxWidth])
-}
-
 // TriggerClick, and return the result html
 // ...then HTML5 parse it, diff the node tree
 // (probably faster and cleaner than anything else)
@@ -189,11 +177,8 @@
 
 	iterateJsElements(doc, func(src, inlineCode string) {
 		if strings.TrimSpace(inlineCode) != "" {
-			log.Infof("JS.Scripts: inline code:")
-			printCode(inlineCode, 20)
 			codes = append(codes, inlineCode)
 		} else if c, ok := downloads[src]; ok {
-			log.Infof("JS.Scripts: referenced code (%v)", src)
 			codes = append(codes, c)
 		}
 	})
--- a/js/js_test.go
+++ b/js/js_test.go
@@ -20,9 +20,7 @@
 `
 
 func init() {
-	logger.Init()
-	log = &logger.Logger{Debug: true}
-	fs.SetLogger(log)
+	log.Debug = true
 	go fs.Srv9p()
 }
 
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -1,4 +1,4 @@
-package logger
+package log
 
 import (
 	"fmt"
@@ -9,20 +9,32 @@
 )
 
 // Sink for Go's log pkg
-var Sink io.Writer
-var Quiet bool
-var Log *Logger
-var gl *goLog.Logger
+var (
+	Sink io.Writer
+	quiet bool
+	gl *goLog.Logger
 
-func Init() {
+	Debug    bool
+
+	mu       sync.Mutex
+	last     string
+	lastSev  int
+	repeated int
+)
+
+func init() {
 	gl = goLog.New(os.Stderr, "", goLog.LstdFlags)
-	Log = &Logger{}
-	if Quiet {
-		Sink = &NullWriter{}
-		goLog.SetOutput(Sink)
-	}
 }
 
+func SetQuiet() {
+	mu.Lock()
+	defer mu.Unlock()
+
+	quiet = true
+	Sink = &NullWriter{}
+	goLog.SetOutput(Sink)
+}
+
 type NullWriter struct{}
 
 func (w *NullWriter) Write(p []byte) (n int, err error) {
@@ -30,32 +42,23 @@
 	return
 }
 
-type Logger struct {
-	Debug    bool
-
-	mu       sync.Mutex
-	last     string
-	lastSev  int
-	repeated int
+func Printf(format string, v ...interface{}) {
+	emit(debug, format, v...)
 }
 
-func (l *Logger) Printf(format string, v ...interface{}) {
-	l.emit(debug, format, v...)
+func Infof(format string, v ...interface{}) {
+	emit(info, format, v...)
 }
 
-func (l *Logger) Infof(format string, v ...interface{}) {
-	l.emit(info, format, v...)
+func Errorf(format string, v ...interface{}) {
+	emit(er, format, v...)
 }
 
-func (l *Logger) Errorf(format string, v ...interface{}) {
-	l.emit(er, format, v...)
-}
-
-func (l *Logger) Fatal(v ...interface{}) {
+func Fatal(v ...interface{}) {
 	gl.Fatal(v...)
 }
 
-func (l *Logger) Fatalf(format string, v ...interface{}) {
+func Fatalf(format string, v ...interface{}) {
 	gl.Fatalf(format, v...)
 }
 
@@ -69,28 +72,28 @@
 	flush
 )
 
-func (l *Logger) Flush() {
-	l.emit(flush, "")
+func Flush() {
+	emit(flush, "")
 }
 
-func (l *Logger) emit(severity int, format string, v ...interface{}) {
-	l.mu.Lock()
-	defer l.mu.Unlock()
+func emit(severity int, format string, v ...interface{}) {
+	mu.Lock()
+	defer mu.Unlock()
 
-	if severity == debug && !l.Debug {
+	if severity == debug && !Debug {
 		return
 	}
-	if (severity != fatal && severity != flush) && Quiet {
+	if (severity != fatal && severity != flush) && quiet {
 		return
 	}
 
 	msg := fmt.Sprintf(format, v...)
 	switch {
-	case l.last == msg && l.lastSev == severity:
-		l.repeated++
-	case l.repeated > 0:
-		goLog.Printf("...and %v more", l.repeated)
-		l.repeated = 0
+	case last == msg && lastSev == severity:
+		repeated++
+	case repeated > 0:
+		goLog.Printf("...and %v more", repeated)
+		repeated = 0
 		fallthrough
 	default:
 		switch severity {
@@ -105,6 +108,6 @@
 		case flush:
 		}
 	}
-	l.last = msg
-	l.lastSev = severity
+	last = msg
+	lastSev = severity
 }
--- a/nodes/nodes.go
+++ b/nodes/nodes.go
@@ -10,11 +10,6 @@
 	"strings"
 )
 
-var log *logger.Logger
-func SetLogger(l *logger.Logger) {
-	log = l
-}
-
 // Node represents a node at the render stage. It
 // represents a subTree or just a single html node.
 type Node struct {
--- a/opossum.go
+++ b/opossum.go
@@ -12,12 +12,6 @@
 	"strings"
 )
 
-var log *logger.Logger
-
-func SetLogger(l *logger.Logger) {
-	log = l
-}
-
 type Fetcher interface {
 	Origin() *url.URL
 
--- a/style/experimental.go
+++ b/style/experimental.go
@@ -8,6 +8,7 @@
 	"image"
 	"github.com/psilva261/opossum"
 	"github.com/psilva261/opossum/img"
+	"github.com/psilva261/opossum/logger"
 	"strings"
 )
 
@@ -113,7 +114,7 @@
 		w := cs.Width()
 		h := cs.Height()
 
-		r, err := img.Load(fetcher, imgUrl, w, h)
+		r, err := img.Load(fetcher, imgUrl, 0, w, h)
 		if err != nil {
 			log.Errorf("bg img load %v: %v", imgUrl, err)
 			return nil
--- a/style/fonts_plan9.go
+++ b/style/fonts_plan9.go
@@ -5,6 +5,7 @@
 import (
 	"9fans.net/go/draw"
 	"fmt"
+	"github.com/psilva261/opossum/logger"
 	"io/fs"
 	"regexp"
 	"strings"
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -20,7 +20,6 @@
 
 var dui *duit.DUI
 var availableFontNames []string
-var log *logger.Logger
 
 var rMinWidth = regexp.MustCompile(`min-width: (\d+)(px|em|rem)`)
 var rMaxWidth = regexp.MustCompile(`max-width: (\d+)(px|em|rem)`)
@@ -55,9 +54,8 @@
 }
 `
 
-func Init(d *duit.DUI, l *logger.Logger) {
+func Init(d *duit.DUI) {
 	dui = d
-	log = l
 
 	initFontserver()
 }
@@ -587,7 +585,7 @@
 		if p, ok := cs.DomTree.Parent(); ok {
 			wp = p.Style().baseWidth()
 		} else {
-			log.Errorf("%% unit used in root element")
+			log.Printf("%% unit used in root element")
 		}
 		f *= 0.01 * float64(wp)
 	default:
@@ -610,6 +608,22 @@
 }
 
 func (cs Map) Width() int {
+	w := cs.width()
+	if w > 0 {
+		if d, ok := cs.Declarations["max-width"]; ok {
+			f, _, err := length(&cs, d.Value)
+			if err != nil {
+				log.Errorf("cannot parse width: %v", err)
+			}
+			if mw := int(f); 0 < mw && mw < w {
+				return int(mw)
+			}
+		}
+	}
+	return w
+}
+
+func (cs Map) width() int {
 	d, ok := cs.Declarations["width"]
 	if ok {
 		f, _, err := length(&cs, d.Value)
@@ -616,7 +630,9 @@
 		if err != nil {
 			log.Errorf("cannot parse width: %v", err)
 		}
-		return int(f)
+		if f > 0 {
+			return int(f)
+		}
 	}
 	if _, ok := cs.DomTree.Parent(); !ok {
 		return WindowWidth
--- a/style/stylesheets_test.go
+++ b/style/stylesheets_test.go
@@ -4,15 +4,9 @@
 	"github.com/chris-ramon/douceur/css"
 	"golang.org/x/net/html"
 	"github.com/mjl-/duit"
-	"github.com/psilva261/opossum/logger"
 	"strings"
 	"testing"
 )
-
-func init() {
-	logger.Init()
-	log = &logger.Logger{Debug: true}
-}
 
 func d(c string) Map {
 	m := Map{