markdown code measurement
This commit is contained in:
parent
d271f37f33
commit
c83a55f26b
3 changed files with 83 additions and 29 deletions
|
|
@ -16,6 +16,10 @@
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md {
|
||||||
|
tab-size: 8;
|
||||||
|
}
|
||||||
|
|
||||||
/* based on https://github.com/sindresorhus/github-markdown-css */
|
/* based on https://github.com/sindresorhus/github-markdown-css */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.md {
|
.md {
|
||||||
|
|
@ -341,15 +345,15 @@
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md tt,
|
/* .md tt,
|
||||||
.md code {
|
.md code {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
} */
|
||||||
|
|
||||||
.md pre {
|
.md pre {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
font-size: 12px;
|
/* font-size: 12px; */
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,7 +639,7 @@
|
||||||
.md tt {
|
.md tt {
|
||||||
padding: 0.2em 0.4em;
|
padding: 0.2em 0.4em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 85%;
|
/* font-size: 85%; */
|
||||||
background-color: var(--color-neutral-muted);
|
background-color: var(--color-neutral-muted);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
@ -675,8 +679,8 @@
|
||||||
.md pre {
|
.md pre {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 85%;
|
/* font-size: 85%;
|
||||||
line-height: 1.45;
|
line-height: 1.45; */
|
||||||
background-color: var(--color-canvas-subtle);
|
background-color: var(--color-canvas-subtle);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"math"
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
|
@ -39,6 +40,9 @@ const (
|
||||||
Padding_pre = 16
|
Padding_pre = 16
|
||||||
MarginBottom_pre = 16
|
MarginBottom_pre = 16
|
||||||
|
|
||||||
|
PaddingTopBottom_code_em = 0.2
|
||||||
|
PaddingLeftRight_code_em = 0.4
|
||||||
|
|
||||||
PaddingLR_blockquote_em = 1.
|
PaddingLR_blockquote_em = 1.
|
||||||
MarginBottom_blockquote = 16
|
MarginBottom_blockquote = 16
|
||||||
BorderLeft_blockquote_em = 0.25
|
BorderLeft_blockquote_em = 0.25
|
||||||
|
|
@ -185,6 +189,11 @@ func hasAncestorElement(n *html.Node, elType string) bool {
|
||||||
|
|
||||||
// measures node dimensions to match rendering with styles in github-markdown.css
|
// measures node dimensions to match rendering with styles in github-markdown.css
|
||||||
func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (width, height, marginTop, marginBottom float64) {
|
func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (width, height, marginTop, marginBottom float64) {
|
||||||
|
var parentElementType string
|
||||||
|
if n.Parent != nil && n.Parent.Type == html.ElementNode {
|
||||||
|
parentElementType = n.Parent.Data
|
||||||
|
}
|
||||||
|
|
||||||
switch n.Type {
|
switch n.Type {
|
||||||
case html.TextNode:
|
case html.TextNode:
|
||||||
if strings.TrimSpace(n.Data) == "" {
|
if strings.TrimSpace(n.Data) == "" {
|
||||||
|
|
@ -196,32 +205,63 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
|
||||||
spaceRune, _ := utf8.DecodeRuneInString(" ")
|
spaceRune, _ := utf8.DecodeRuneInString(" ")
|
||||||
// measure will not include leading or trailing whitespace, so we have to add in the space width
|
// measure will not include leading or trailing whitespace, so we have to add in the space width
|
||||||
spaceWidth := ruler.atlases[font].glyph(spaceRune).advance
|
spaceWidth := ruler.atlases[font].glyph(spaceRune).advance
|
||||||
|
tabWidth := 8 * spaceWidth
|
||||||
|
|
||||||
str := n.Data
|
str := n.Data
|
||||||
hasCodeParent := n.Parent != nil && n.Parent.Type == html.ElementNode && (n.Parent.Data == "pre" || n.Parent.Data == "code")
|
|
||||||
if !hasCodeParent {
|
htmlWhitespace := true
|
||||||
str = strings.ReplaceAll(n.Data, "\n", " ")
|
switch parentElementType {
|
||||||
|
case "pre", "code":
|
||||||
|
htmlWhitespace = false
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(str, " ") {
|
|
||||||
str = strings.TrimPrefix(str, " ")
|
if htmlWhitespace {
|
||||||
if hasPrev(n) {
|
str = strings.ReplaceAll(str, "\n", " ")
|
||||||
spaceWidths += spaceWidth
|
str = strings.ReplaceAll(str, "\t", " ")
|
||||||
|
if strings.HasPrefix(str, " ") {
|
||||||
|
str = strings.TrimPrefix(str, " ")
|
||||||
|
if hasPrev(n) {
|
||||||
|
spaceWidths += spaceWidth
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if strings.HasSuffix(str, " ") {
|
||||||
if strings.HasSuffix(str, " ") {
|
str = strings.TrimSuffix(str, " ")
|
||||||
str = strings.TrimSuffix(str, " ")
|
if hasNext(n) {
|
||||||
if hasNext(n) {
|
spaceWidths += spaceWidth
|
||||||
spaceWidths += spaceWidth
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isNotSpace := func(r rune) bool {
|
||||||
|
return !unicode.IsSpace(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex := strings.IndexFunc(str, isNotSpace)
|
||||||
|
endIndex := strings.LastIndexFunc(str, isNotSpace)
|
||||||
|
|
||||||
|
if startIndex != -1 && endIndex != -1 {
|
||||||
|
for i, r := range str {
|
||||||
|
// skip over runes in middle
|
||||||
|
if i >= startIndex && i <= endIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// measure width of leading/trailing whitespace
|
||||||
|
switch r {
|
||||||
|
case ' ':
|
||||||
|
spaceWidths += spaceWidth
|
||||||
|
case '\t':
|
||||||
|
spaceWidths += tabWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str[startIndex : endIndex+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
w, h := ruler.MeasurePrecise(font, str)
|
w, h := ruler.MeasurePrecise(font, str)
|
||||||
w += spaceWidths
|
// fmt.Printf("%d:'%s' width %v (%v) height %v fontStyle %s fontSize %v family %v\n", depth, n.Data, w, w+spaceWidths, h, font.Style, font.Size, font.Family)
|
||||||
// fmt.Printf("%d:%s width %v height %v fontStyle %s\n", depth, n.Data, w, h, font.Style)
|
|
||||||
if h > 0 && h < MarkdownLineHeightPx {
|
return w + spaceWidths, h, 0, 0
|
||||||
h = MarkdownLineHeightPx
|
|
||||||
}
|
|
||||||
return w, h, 0, 0
|
|
||||||
case html.ElementNode:
|
case html.ElementNode:
|
||||||
// fmt.Printf("%d: %v node\n", depth, n.Data)
|
// fmt.Printf("%d: %v node\n", depth, n.Data)
|
||||||
switch n.Data {
|
switch n.Data {
|
||||||
|
|
@ -237,7 +277,8 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
|
||||||
case "b", "strong":
|
case "b", "strong":
|
||||||
font.Style = d2fonts.FONT_STYLE_BOLD
|
font.Style = d2fonts.FONT_STYLE_BOLD
|
||||||
case "pre", "code":
|
case "pre", "code":
|
||||||
// TODO monospaced font
|
font.Family = d2fonts.SourceCodePro
|
||||||
|
font.Style = d2fonts.FONT_STYLE_REGULAR
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.FirstChild != nil {
|
if n.FirstChild != nil {
|
||||||
|
|
@ -284,10 +325,10 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
|
||||||
|
|
||||||
switch n.Data {
|
switch n.Data {
|
||||||
case "blockquote":
|
case "blockquote":
|
||||||
width += float64(font.Size) * (2*PaddingLR_blockquote_em + BorderLeft_blockquote_em)
|
width += (2*PaddingLR_blockquote_em + BorderLeft_blockquote_em) * float64(font.Size)
|
||||||
marginBottom = go2.Max(marginBottom, MarginBottom_blockquote)
|
marginBottom = go2.Max(marginBottom, MarginBottom_blockquote)
|
||||||
case "p":
|
case "p":
|
||||||
if n.Parent != nil && n.Parent.Type == html.ElementNode && n.Parent.Data == "li" {
|
if parentElementType == "li" {
|
||||||
marginTop = go2.Max(marginTop, MarginTop_li_p)
|
marginTop = go2.Max(marginTop, MarginTop_li_p)
|
||||||
}
|
}
|
||||||
marginBottom = go2.Max(marginBottom, MarginBottom_p)
|
marginBottom = go2.Max(marginBottom, MarginBottom_p)
|
||||||
|
|
@ -296,7 +337,7 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
|
||||||
marginBottom = go2.Max(marginBottom, MarginBottom_h)
|
marginBottom = go2.Max(marginBottom, MarginBottom_h)
|
||||||
switch n.Data {
|
switch n.Data {
|
||||||
case "h1", "h2":
|
case "h1", "h2":
|
||||||
height += float64(HeaderToFontSize[n.Data]) * PaddingBottom_h1_h2_em
|
height += PaddingBottom_h1_h2_em * float64(font.Size)
|
||||||
}
|
}
|
||||||
case "li":
|
case "li":
|
||||||
width += PaddingLeft_ul_ol
|
width += PaddingLeft_ul_ol
|
||||||
|
|
@ -314,11 +355,20 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
|
||||||
width += 2 * Padding_pre
|
width += 2 * Padding_pre
|
||||||
height += 2 * Padding_pre
|
height += 2 * Padding_pre
|
||||||
marginBottom = go2.Max(marginBottom, MarginBottom_pre)
|
marginBottom = go2.Max(marginBottom, MarginBottom_pre)
|
||||||
|
case "code":
|
||||||
|
if parentElementType != "pre" {
|
||||||
|
width += 2 * PaddingLeftRight_code_em * float64(font.Size)
|
||||||
|
height += 2 * PaddingTopBottom_code_em * float64(font.Size)
|
||||||
|
}
|
||||||
case "hr":
|
case "hr":
|
||||||
height += Height_hr
|
height += Height_hr
|
||||||
marginTop = go2.Max(marginTop, MarginTopBottom_hr)
|
marginTop = go2.Max(marginTop, MarginTopBottom_hr)
|
||||||
marginBottom = go2.Max(marginBottom, MarginTopBottom_hr)
|
marginBottom = go2.Max(marginBottom, MarginTopBottom_hr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if height > 0 && height < MarkdownLineHeightPx {
|
||||||
|
height = MarkdownLineHeightPx
|
||||||
|
}
|
||||||
// fmt.Printf("%d:%s width %v height %v mt %v mb %v\n", depth, n.Data, width, height, marginTop, marginBottom)
|
// fmt.Printf("%d:%s width %v height %v mt %v mb %v\n", depth, n.Data, width, height, marginTop, marginBottom)
|
||||||
}
|
}
|
||||||
return width, height, marginTop, marginBottom
|
return width, height, marginTop, marginBottom
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ func testTodo(t *testing.T) {
|
||||||
skip: false,
|
skip: false,
|
||||||
name: "md_code_inline",
|
name: "md_code_inline",
|
||||||
script: `md: |md
|
script: `md: |md
|
||||||
` + "`" + "code`" + `
|
` + "`code`" + `
|
||||||
|
|
|
|
||||||
a -> md -> b
|
a -> md -> b
|
||||||
`,
|
`,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue