markdown code measurement

This commit is contained in:
Gavin Nishizawa 2022-11-07 15:18:16 -08:00
parent d271f37f33
commit c83a55f26b
3 changed files with 83 additions and 29 deletions

View file

@ -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;
} }

View file

@ -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

View file

@ -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
`, `,