markdown code tabsize and measurement accuracy

This commit is contained in:
Gavin Nishizawa 2022-11-07 18:07:04 -08:00
parent c83a55f26b
commit bffb018651
12 changed files with 158 additions and 124 deletions

View file

@ -828,12 +828,14 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
} }
var dims *d2target.TextDimensions var dims *d2target.TextDimensions
var innerLabelPadding = 5
if obj.Attributes.Shape.Value == d2target.ShapeText { if obj.Attributes.Shape.Value == d2target.ShapeText {
var err error var err error
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text()) dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text())
if err != nil { if err != nil {
return err return err
} }
innerLabelPadding = 0
} else { } else {
dims = getTextDimensions(mtexts, ruler, obj.Text()) dims = getTextDimensions(mtexts, ruler, obj.Text())
} }
@ -855,7 +857,6 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
} }
} }
const innerLabelPadding = 5
dims.Width += innerLabelPadding dims.Width += innerLabelPadding
dims.Height += innerLabelPadding dims.Height += innerLabelPadding
obj.LabelDimensions = *dims obj.LabelDimensions = *dims

View file

@ -17,7 +17,7 @@
} }
.md { .md {
tab-size: 8; tab-size: 4;
} }
/* based on https://github.com/sindresorhus/github-markdown-css */ /* based on https://github.com/sindresorhus/github-markdown-css */

View file

@ -205,17 +205,17 @@ 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 tabWidth := TAB_SIZE * spaceWidth
str := n.Data str := n.Data
htmlWhitespace := true isCode := false
switch parentElementType { switch parentElementType {
case "pre", "code": case "pre", "code":
htmlWhitespace = false isCode = true
} }
if htmlWhitespace { if !isCode {
str = strings.ReplaceAll(str, "\n", " ") str = strings.ReplaceAll(str, "\n", " ")
str = strings.ReplaceAll(str, "\t", " ") str = strings.ReplaceAll(str, "\t", " ")
if strings.HasPrefix(str, " ") { if strings.HasPrefix(str, " ") {
@ -258,7 +258,13 @@ func (ruler *Ruler) measureNode(depth int, n *html.Node, font d2fonts.Font) (wid
} }
} }
if isCode {
ruler.boundsWithDot = true
}
w, h := ruler.MeasurePrecise(font, str) w, h := ruler.MeasurePrecise(font, str)
if isCode {
ruler.boundsWithDot = false
}
// 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 (%v) height %v fontStyle %s fontSize %v family %v\n", depth, n.Data, w, w+spaceWidths, h, font.Style, font.Size, font.Family)
return w + spaceWidths, h, 0, 0 return w + spaceWidths, h, 0, 0

View file

@ -14,6 +14,8 @@ import (
"oss.terrastruct.com/d2/lib/geo" "oss.terrastruct.com/d2/lib/geo"
) )
const TAB_SIZE = 4
// ASCII is a set of all ASCII runes. These runes are codepoints from 32 to 127 inclusive. // ASCII is a set of all ASCII runes. These runes are codepoints from 32 to 127 inclusive.
var ASCII []rune var ASCII []rune
@ -77,6 +79,9 @@ type Ruler struct {
buf []byte buf []byte
prevR rune prevR rune
bounds *rect bounds *rect
// when drawing text also union Ruler.bounds with Dot
boundsWithDot bool
} }
// New creates a new Ruler capable of drawing runes contained in the provided atlas. Orig and Dot // New creates a new Ruler capable of drawing runes contained in the provided atlas. Orig and Dot
@ -118,7 +123,7 @@ func NewRuler() (*Ruler, error) {
atlas := NewAtlas(face, ASCII) atlas := NewAtlas(face, ASCII)
atlases[font] = atlas atlases[font] = atlas
lineHeights[font] = atlas.lineHeight lineHeights[font] = atlas.lineHeight
tabWidths[font] = atlas.glyph(' ').advance * 4 tabWidths[font] = atlas.glyph(' ').advance * TAB_SIZE
} }
} }
} }
@ -199,10 +204,15 @@ func (txt *Ruler) drawBuf(font d2fonts.Font) {
txt.prevR = r txt.prevR = r
if txt.bounds.w()*txt.bounds.h() == 0 { if txt.boundsWithDot {
txt.bounds = bounds txt.bounds = txt.bounds.union(&rect{txt.Dot, txt.Dot})
} else {
txt.bounds = txt.bounds.union(bounds) txt.bounds = txt.bounds.union(bounds)
} else {
if txt.bounds.w()*txt.bounds.h() == 0 {
txt.bounds = bounds
} else {
txt.bounds = txt.bounds.union(bounds)
}
} }
} }
} }

View file

@ -95,6 +95,12 @@ _italics are all measured correctly_
` `
**Note:** **Note:**
`: {37, 24}, `: {37, 24},
`a`: {7, 24},
`w`: {12, 24},
`ww`: {24, 24},
"`inline code`": {119, 27},
"`code`": {52, 27},
"`a`": {23, 27},
} }
func TestTextMeasureMarkdown(t *testing.T) { func TestTextMeasureMarkdown(t *testing.T) {

View file

@ -8,8 +8,8 @@
"x": 0, "x": 0,
"y": 226 "y": 226
}, },
"width": 157, "width": 224,
"height": 130, "height": 125,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
@ -35,14 +35,14 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 157, "labelWidth": 224,
"labelHeight": 130 "labelHeight": 125
}, },
{ {
"id": "a", "id": "a",
"type": "", "type": "",
"pos": { "pos": {
"x": 22, "x": 56,
"y": 0 "y": 0
}, },
"width": 113, "width": 113,
@ -80,8 +80,8 @@
"id": "b", "id": "b",
"type": "", "type": "",
"pos": { "pos": {
"x": 22, "x": 56,
"y": 456 "y": 451
}, },
"width": 113, "width": 113,
"height": 126, "height": 126,
@ -142,19 +142,19 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 78.5, "x": 112,
"y": 126 "y": 126
}, },
{ {
"x": 78.5, "x": 112,
"y": 166 "y": 166
}, },
{ {
"x": 78.5, "x": 112,
"y": 186 "y": 186
}, },
{ {
"x": 78.5, "x": 112,
"y": 226 "y": 226
} }
], ],
@ -189,20 +189,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 78.5, "x": 112,
"y": 356 "y": 351
}, },
{ {
"x": 78.5, "x": 112,
"y": 396 "y": 391
}, },
{ {
"x": 78.5, "x": 112,
"y": 416 "y": 411
}, },
{ {
"x": 78.5, "x": 112,
"y": 456 "y": 451
} }
], ],
"isCurve": true, "isCurve": true,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 660 KiB

After

Width:  |  Height:  |  Size: 841 KiB

View file

@ -8,8 +8,8 @@
"x": 0, "x": 0,
"y": 226 "y": 226
}, },
"width": 213, "width": 208,
"height": 29, "height": 24,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
@ -35,14 +35,14 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 213, "labelWidth": 208,
"labelHeight": 29 "labelHeight": 24
}, },
{ {
"id": "a", "id": "a",
"type": "", "type": "",
"pos": { "pos": {
"x": 50, "x": 48,
"y": 0 "y": 0
}, },
"width": 113, "width": 113,
@ -80,8 +80,8 @@
"id": "b", "id": "b",
"type": "", "type": "",
"pos": { "pos": {
"x": 50, "x": 48,
"y": 355 "y": 350
}, },
"width": 113, "width": 113,
"height": 126, "height": 126,
@ -142,19 +142,19 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 106.5, "x": 104,
"y": 126 "y": 126
}, },
{ {
"x": 106.5, "x": 104,
"y": 166 "y": 166
}, },
{ {
"x": 106.5, "x": 104,
"y": 186 "y": 186
}, },
{ {
"x": 106.5, "x": 104,
"y": 226 "y": 226
} }
], ],
@ -189,20 +189,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 106.5, "x": 104,
"y": 255 "y": 250
}, },
{ {
"x": 106.5, "x": 104,
"y": 295 "y": 290
}, },
{ {
"x": 106.5, "x": 104,
"y": 315 "y": 310
}, },
{ {
"x": 106.5, "x": 104,
"y": 355 "y": 350
} }
], ],
"isCurve": true, "isCurve": true,

View file

@ -2,7 +2,7 @@
<svg <svg
style="background: white;" style="background: white;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css"> width="408" height="676" viewBox="-100 -100 408 676"><style type="text/css">
<![CDATA[ <![CDATA[
.shape { .shape {
shape-rendering: geometricPrecision; shape-rendering: geometricPrecision;
@ -22,6 +22,18 @@ width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css">
font-family: "font-bold"; font-family: "font-bold";
} }
.md code,
.md kbd,
.md pre,
.md samp {
font-family: "font-mono";
font-size: 1em;
}
.md {
tab-size: 4;
}
/* 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 {
@ -145,14 +157,6 @@ width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css">
background-color: var(--color-canvas-default); background-color: var(--color-canvas-default);
} }
.md code,
.md kbd,
.md pre,
.md samp {
font-family: monospace, monospace;
font-size: 1em;
}
.md figure { .md figure {
margin: 1em 40px; margin: 1em 40px;
} }
@ -355,19 +359,15 @@ width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css">
margin-left: 0; margin-left: 0;
} }
.md tt, /* .md tt,
.md code { .md code {
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono,
monospace;
font-size: 12px; font-size: 12px;
} } */
.md pre { .md pre {
margin-top: 0; margin-top: 0;
margin-bottom: 0; margin-bottom: 0;
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, /* font-size: 12px; */
monospace;
font-size: 12px;
word-wrap: normal; word-wrap: normal;
} }
@ -653,7 +653,7 @@ width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css">
.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;
} }
@ -693,8 +693,8 @@ width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css">
.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;
} }
@ -797,11 +797,11 @@ width="413" height="681" viewBox="-100 -100 413 681"><style type="text/css">
.md .contains-task-list:dir(rtl) .task-list-item-checkbox { .md .contains-task-list:dir(rtl) .task-list-item-checkbox {
margin: 0 -1.6em 0.25em 0.2em; margin: 0 -1.6em 0.25em 0.2em;
} }
</style><g><foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" x="0.000000" y="226.000000" width="213" height="29"><div xmlns="http://www.w3.org/1999/xhtml" class="md"><p>{ </style><g><foreignObject requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" x="0.000000" y="226.000000" width="208" height="24"><div xmlns="http://www.w3.org/1999/xhtml" class="md"><p>{
indented: &quot;block&quot;, indented: &quot;block&quot;,
of: &quot;json&quot;, of: &quot;json&quot;,
}</p> }</p>
</div></foreignObject></g><rect class="shape" x="50" y="0" width="113" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /><text class="text-bold" x="106.500000" y="66.000000" style="text-anchor:middle;font-size:16px;fill:black">a</text><rect class="shape" x="50" y="355" width="113" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /><text class="text-bold" x="106.500000" y="421.000000" style="text-anchor:middle;font-size:16px;fill:black">b</text><marker id="mk-3990223579" markerWidth="10.000000" markerHeight="12.000000" refX="8.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon class="connection" fill="#0D32B2" stroke-width="2" points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" /> </marker><path d="M 106.500000 127.000000 C 106.500000 166.000000 106.500000 186.000000 106.500000 223.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /><path d="M 106.500000 256.000000 C 106.500000 295.000000 106.500000 315.000000 106.500000 352.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /><style type="text/css"><![CDATA[ </div></foreignObject></g><rect class="shape" x="48" y="0" width="113" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /><text class="text-bold" x="104.500000" y="66.000000" style="text-anchor:middle;font-size:16px;fill:black">a</text><rect class="shape" x="48" y="350" width="113" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /><text class="text-bold" x="104.500000" y="416.000000" style="text-anchor:middle;font-size:16px;fill:black">b</text><marker id="mk-3990223579" markerWidth="10.000000" markerHeight="12.000000" refX="8.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon class="connection" fill="#0D32B2" stroke-width="2" points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" /> </marker><path d="M 104.000000 127.000000 C 104.000000 166.000000 104.000000 186.000000 104.000000 223.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /><path d="M 104.000000 251.000000 C 104.000000 290.000000 104.000000 310.000000 104.000000 347.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /><style type="text/css"><![CDATA[
.text { .text {
font-family: "font-regular"; font-family: "font-regular";
} }

Before

Width:  |  Height:  |  Size: 660 KiB

After

Width:  |  Height:  |  Size: 660 KiB

View file

@ -5,11 +5,11 @@
"id": "md", "id": "md",
"type": "text", "type": "text",
"pos": { "pos": {
"x": 38, "x": 31,
"y": 226 "y": 226
}, },
"width": 38, "width": 52,
"height": 29, "height": 27,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
@ -35,8 +35,8 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 38, "labelWidth": 52,
"labelHeight": 29 "labelHeight": 27
}, },
{ {
"id": "a", "id": "a",
@ -81,7 +81,7 @@
"type": "", "type": "",
"pos": { "pos": {
"x": 0, "x": 0,
"y": 355 "y": 353
}, },
"width": 113, "width": 113,
"height": 126, "height": 126,
@ -190,19 +190,19 @@
"route": [ "route": [
{ {
"x": 56.5, "x": 56.5,
"y": 255 "y": 253
}, },
{ {
"x": 56.5, "x": 56.5,
"y": 295 "y": 293
}, },
{ {
"x": 56.5, "x": 56.5,
"y": 315 "y": 313
}, },
{ {
"x": 56.5, "x": 56.5,
"y": 355 "y": 353
} }
], ],
"isCurve": true, "isCurve": true,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 660 KiB

After

Width:  |  Height:  |  Size: 841 KiB

View file

@ -10,7 +10,6 @@ func testTodo(t *testing.T) {
// https://github.com/terrastruct/d2/issues/24 // https://github.com/terrastruct/d2/issues/24
// string monstrosity from not being able to escape backticks within string literals // string monstrosity from not being able to escape backticks within string literals
{ {
skip: false,
name: "md_code_inline", name: "md_code_inline",
script: `md: |md script: `md: |md
` + "`code`" + ` ` + "`code`" + `
@ -19,7 +18,6 @@ a -> md -> b
`, `,
}, },
{ {
skip: false,
name: "md_code_block_fenced", name: "md_code_block_fenced",
script: `md: |md script: `md: |md
` + "```" + ` ` + "```" + `
@ -33,7 +31,6 @@ a -> md -> b
`, `,
}, },
{ {
skip: false,
name: "md_code_block_indented", name: "md_code_block_indented",
script: `md: |md script: `md: |md
{ {