ref: ac353fabcd30e7eba106cb11d05ee8e08a749173
parent: 5aba680979d4c267ad5b279810d24c85fdf208b1
author: Philip Silva <[email protected]>
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{