From 37554dd719034b911f15bd0b1c4bcacccdc9b647 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 10 Sep 2024 11:20:08 -0400 Subject: [PATCH] Add measurement stage --- measure.go | 45 +++++++++++++++++++++++++++++++ measure_test.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 measure.go create mode 100644 measure_test.go diff --git a/measure.go b/measure.go new file mode 100644 index 0000000..89345e6 --- /dev/null +++ b/measure.go @@ -0,0 +1,45 @@ +package typeset + +import "golang.org/x/image/font" +import "golang.org/x/image/math/fixed" + +func measure (tokens []token, face font.Face) { + var lastRune rune + for index, token := range tokens { + var x fixed.Int26_6 + for index, runl := range token.runes { + advance, ok := face.GlyphAdvance(runl.run) + if !ok { advance = tofuAdvance(face) } + advance += face.Kern(lastRune, runl.run) + + runl.x = x + x += advance + lastRune = runl.run + token.runes[index] = runl + } + token.width = x + tokens[index] = token + } +} + +const tofuStandinRune = 'M' +const fallbackTofuAdvance = 16 +const fallbackTofuWidth = 14 +const fallbackTofuAscend = 16 + +func tofuAdvance (face font.Face) fixed.Int26_6 { + if advance, ok := face.GlyphAdvance(tofuStandinRune); ok { + return advance + } else { + return fallbackTofuAdvance + } +} + +func tofuBounds (face font.Face) (fixed.Rectangle26_6, fixed.Int26_6) { + if bounds, advance, ok := face.GlyphBounds(tofuStandinRune); ok { + return bounds, advance + } else { + return fixed.R(0, -fallbackTofuAscend, fallbackTofuWidth, 0), + fallbackTofuAdvance + } +} diff --git a/measure_test.go b/measure_test.go new file mode 100644 index 0000000..99d00be --- /dev/null +++ b/measure_test.go @@ -0,0 +1,70 @@ +package typeset + +import "testing" +import "golang.org/x/image/math/fixed" +import "golang.org/x/image/font/basicfont" + +const basicfontFace7x13advance = 7 + +func tkw (kind tokenKind, value string, width fixed.Int26_6) token { + tok := tk(kind, value) + tok.width = width + for index, runl := range tok.runes { + runl.x = fixed.I(basicfontFace7x13advance * index) + tok.runes[index] = runl + } + return tok +} + +func TestMeasure (test *testing.T) { + // ---- processing ---- + tokens := []token { + tk(tokenKindWord, "hello"), + tk(tokenKindSpace, " "), + tk(tokenKindWord, "\rworld!"), + tk(tokenKindLineBreak, "\n"), + tk(tokenKindWord, "foo"), + tk(tokenKindLineBreak, "\n"), + tk(tokenKindLineBreak, "\r\n"), + tk(tokenKindWord, "bar"), + tk(tokenKindTab, "\t"), + tk(tokenKindWord, "baz"), + tk(tokenKindTab, "\t\t"), + tk(tokenKindWord, "something"), + } + measure(tokens, basicfont.Face7x13) + + // ---- correct data ---- + correctTokens := []token { + tkw(tokenKindWord, "hello", fixed.I(35)), + tkw(tokenKindSpace, " ", fixed.I( 7)), + tkw(tokenKindWord, "\rworld!", fixed.I(49)), + tkw(tokenKindLineBreak, "\n", fixed.I( 7)), + tkw(tokenKindWord, "foo", fixed.I(21)), + tkw(tokenKindLineBreak, "\n", fixed.I( 7)), + tkw(tokenKindLineBreak, "\r\n", fixed.I(14)), + tkw(tokenKindWord, "bar", fixed.I(21)), + tkw(tokenKindTab, "\t", fixed.I( 7)), + tkw(tokenKindWord, "baz", fixed.I(21)), + tkw(tokenKindTab, "\t\t", fixed.I(14)), + tkw(tokenKindWord, "something", fixed.I(63)), + } + + // ---- testing ---- + if len(tokens) != len(correctTokens) { + test.Logf("len(tokens) != len(correctTokens): %d, %d", len(tokens), len(correctTokens)) + test.Log("GOT") + logTokens(test, tokens) + test.Log("CORRECT") + logTokens(test, correctTokens) + test.FailNow() + } + if !compareTokens(tokens, correctTokens) { + test.Log("tokens != correctTokens:") + test.Log("GOT") + logTokens(test, tokens) + test.Log("CORRECT") + logTokens(test, correctTokens) + test.FailNow() + } +}