2022-11-27 01:54:41PM

This commit is contained in:
Alexander Wang 2022-11-27 13:54:41 -08:00
parent 2fee03e29d
commit 72dcdf9cc4
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
9 changed files with 1985 additions and 186 deletions

View file

@ -377,7 +377,7 @@ func (c *compiler) applyScalar(attrs *d2graph.Attributes, reserved string, box d
if ok { if ok {
attrs.Language = fullTag attrs.Language = fullTag
} }
if attrs.Language == "markdown" { if attrs.Language == "markdown" || attrs.Language == "latex" {
attrs.Shape.Value = d2target.ShapeText attrs.Shape.Value = d2target.ShapeText
} else { } else {
attrs.Shape.Value = d2target.ShapeCode attrs.Shape.Value = d2target.ShapeCode
@ -549,6 +549,7 @@ func (c *compiler) compileFlatKey(k *d2ast.KeyPath) ([]string, string, bool) {
// TODO add more, e.g. C, bash // TODO add more, e.g. C, bash
var ShortToFullLanguageAliases = map[string]string{ var ShortToFullLanguageAliases = map[string]string{
"md": "markdown", "md": "markdown",
"tex": "latex",
"js": "javascript", "js": "javascript",
"go": "golang", "go": "golang",
"py": "python", "py": "python",

View file

@ -11,6 +11,7 @@ import (
"oss.terrastruct.com/d2/d2format" "oss.terrastruct.com/d2/d2format"
"oss.terrastruct.com/d2/d2parser" "oss.terrastruct.com/d2/d2parser"
"oss.terrastruct.com/d2/d2renderers/d2fonts" "oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
"oss.terrastruct.com/d2/d2renderers/textmeasure" "oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target" "oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/d2themes" "oss.terrastruct.com/d2/d2themes"
@ -833,11 +834,19 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
var dims *d2target.TextDimensions var dims *d2target.TextDimensions
var innerLabelPadding = 5 var innerLabelPadding = 5
if obj.Attributes.Shape.Value == d2target.ShapeText { if obj.Attributes.Shape.Value == d2target.ShapeText {
if obj.Attributes.Language == "latex" {
width, height, err := d2latex.Measure(obj.Text().Text)
if err != nil {
return err
}
dims = d2target.NewTextDimensions(width, height)
} else {
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 innerLabelPadding = 0
} else { } else {
dims = getTextDimensions(mtexts, ruler, obj.Text()) dims = getTextDimensions(mtexts, ruler, obj.Text())

View file

@ -3,6 +3,9 @@ package d2latex
import ( import (
_ "embed" _ "embed"
"fmt" "fmt"
"math"
"regexp"
"strconv"
v8 "rogchap.com/v8go" v8 "rogchap.com/v8go"
) )
@ -16,7 +19,9 @@ var setupJS string
//go:embed mathjax.js //go:embed mathjax.js
var mathjaxJS string var mathjaxJS string
func SVG(s string) (string, error) { var svgRe = regexp.MustCompile(`<svg[^>]+width="([0-9\.]+)ex" height="([0-9\.]+)ex"[^>]+>`)
func Render(s string) (string, error) {
v8ctx := v8.NewContext() v8ctx := v8.NewContext()
if _, err := v8ctx.RunScript(polyfillsJS, "polyfills.js"); err != nil { if _, err := v8ctx.RunScript(polyfillsJS, "polyfills.js"); err != nil {
@ -41,3 +46,29 @@ func SVG(s string) (string, error) {
return val.String(), nil return val.String(), nil
} }
func Measure(s string) (width, height int, _ error) {
svg, err := Render(s)
if err != nil {
return 0, 0, err
}
dims := svgRe.FindAllStringSubmatch(svg, -1)
if len(dims) != 1 || len(dims[0]) != 3 {
return 0, 0, fmt.Errorf("svg parsing failed for latex: %v", svg)
}
wEx := dims[0][1]
hEx := dims[0][2]
wf, err := strconv.ParseFloat(wEx, 64)
if err != nil {
return 0, 0, fmt.Errorf("svg parsing failed for latex: %v", svg)
}
hf, err := strconv.ParseFloat(hEx, 64)
if err != nil {
return 0, 0, fmt.Errorf("svg parsing failed for latex: %v", svg)
}
return int(math.Ceil(wf * 8)), int(math.Ceil(hf * 8)), nil
}

View file

@ -18,7 +18,6 @@ import (
"github.com/alecthomas/chroma/formatters" "github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers" "github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles" "github.com/alecthomas/chroma/styles"
"github.com/davecgh/go-spew/spew"
"oss.terrastruct.com/d2/d2renderers/d2fonts" "oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex" "oss.terrastruct.com/d2/d2renderers/d2latex"
@ -662,19 +661,6 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error {
switch targetShape.Type { switch targetShape.Type {
case d2target.ShapeCode: case d2target.ShapeCode:
if targetShape.Language == "latex" {
spew.Dump(targetShape.Label)
render, err := d2latex.SVG(targetShape.Label)
if err != nil {
spew.Dump(err)
return err
}
fmt.Fprintf(writer, `<g transform="translate(%f %f)" style="opacity:%f">`, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
// render = strings.Replace(render, "svg", "g", -1)
spew.Dump(render)
fmt.Fprintf(writer, render)
fmt.Fprintf(writer, "</g>")
} else {
lexer := lexers.Get(targetShape.Language) lexer := lexers.Get(targetShape.Language)
if lexer == nil { if lexer == nil {
return fmt.Errorf("code snippet lexer for %s not found", targetShape.Language) return fmt.Errorf("code snippet lexer for %s not found", targetShape.Language)
@ -715,8 +701,16 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error {
fmt.Fprint(writer, "</text>") fmt.Fprint(writer, "</text>")
} }
fmt.Fprintf(writer, "</g></g>") fmt.Fprintf(writer, "</g></g>")
}
case d2target.ShapeText: case d2target.ShapeText:
if targetShape.Language == "latex" {
render, err := d2latex.Render(targetShape.Label)
if err != nil {
return err
}
fmt.Fprintf(writer, `<g transform="translate(%f %f)" style="opacity:%f">`, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
fmt.Fprintf(writer, render)
fmt.Fprintf(writer, "</g>")
} else {
render, err := textmeasure.RenderMarkdown(targetShape.Label) render, err := textmeasure.RenderMarkdown(targetShape.Label)
if err != nil { if err != nil {
return err return err
@ -728,6 +722,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error {
render = strings.ReplaceAll(render, "<hr>", "<hr />") render = strings.ReplaceAll(render, "<hr>", "<hr />")
fmt.Fprintf(writer, `<div xmlns="http://www.w3.org/1999/xhtml" class="md">%v</div>`, render) fmt.Fprintf(writer, `<div xmlns="http://www.w3.org/1999/xhtml" class="md">%v</div>`, render)
fmt.Fprint(writer, `</foreignObject></g>`) fmt.Fprint(writer, `</foreignObject></g>`)
}
default: default:
fontColor := "black" fontColor := "black"
if targetShape.Color != "" { if targetShape.Color != "" {

View file

@ -3,20 +3,20 @@
"shapes": [ "shapes": [
{ {
"id": "a", "id": "a",
"type": "code", "type": "text",
"pos": { "pos": {
"x": 0, "x": 0,
"y": 44 "y": 194
}, },
"width": 1022, "width": 154,
"height": 38, "height": 41,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "#FFFFFF", "fill": "#FFFFFF",
"stroke": "#0A0F25", "stroke": "#0D32B2",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
"multiple": false, "multiple": false,
@ -35,25 +35,25 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 1022, "labelWidth": 154,
"labelHeight": 38 "labelHeight": 41
}, },
{ {
"id": "b", "id": "b",
"type": "code", "type": "text",
"pos": { "pos": {
"x": 1082, "x": 214,
"y": 44 "y": 205
}, },
"width": 93, "width": 65,
"height": 38, "height": 18,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "#FFFFFF", "fill": "#FFFFFF",
"stroke": "#0A0F25", "stroke": "#0D32B2",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
"multiple": false, "multiple": false,
@ -72,15 +72,52 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 93, "labelWidth": 65,
"labelHeight": 38 "labelHeight": 18
},
{
"id": "z",
"type": "text",
"pos": {
"x": 72,
"y": 0
},
"width": 179,
"height": 51,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "gibberish\\\\; math:\\\\sum_{i=0}^\\\\infty i^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 179,
"labelHeight": 51
}, },
{ {
"id": "c", "id": "c",
"type": "", "type": "",
"pos": { "pos": {
"x": 1022, "x": 140,
"y": 226 "y": 377
}, },
"width": 214, "width": 214,
"height": 126, "height": 126,
@ -117,8 +154,8 @@
"id": "sugar", "id": "sugar",
"type": "", "type": "",
"pos": { "pos": {
"x": 1235, "x": 339,
"y": 0 "y": 151
}, },
"width": 146, "width": 146,
"height": 126, "height": 126,
@ -155,8 +192,8 @@
"id": "solution", "id": "solution",
"type": "", "type": "",
"pos": { "pos": {
"x": 1047, "x": 165,
"y": 452 "y": 603
}, },
"width": 164, "width": 164,
"height": 126, "height": 126,
@ -191,6 +228,100 @@
} }
], ],
"connections": [ "connections": [
{
"id": "(z -> a)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "a",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 133.12582781456953,
"y": 51
},
{
"x": 88.2251655629139,
"y": 91
},
{
"x": 77,
"y": 119.5
},
{
"x": 77,
"y": 193.5
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(z -> b)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "b",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 190.37417218543047,
"y": 51
},
{
"x": 235.2748344370861,
"y": 91
},
{
"x": 246.5,
"y": 121.8
},
{
"x": 246.5,
"y": 205
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{ {
"id": "(a -> c)[0]", "id": "(a -> c)[0]",
"src": "a", "src": "a",
@ -217,20 +348,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 511, "x": 77,
"y": 82 "y": 235.5
}, },
{ {
"x": 511, "x": 77,
"y": 157.2 "y": 308.7
}, },
{ {
"x": 613.1, "x": 92,
"y": 194.6838866396761 "y": 337
}, },
{ {
"x": 1021.5, "x": 152,
"y": 269.41943319838055 "y": 377
} }
], ],
"isCurve": true, "isCurve": true,
@ -264,20 +395,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1128.5, "x": 246.5,
"y": 82 "y": 223
}, },
{ {
"x": 1128.5, "x": 246.5,
"y": 157.2 "y": 306.2
}, },
{ {
"x": 1128.5, "x": 246.5,
"y": 186 "y": 337
}, },
{ {
"x": 1128.5, "x": 246.5,
"y": 226 "y": 377
} }
], ],
"isCurve": true, "isCurve": true,
@ -311,20 +442,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1308, "x": 412,
"y": 126 "y": 277
}, },
{ {
"x": 1308, "x": 412,
"y": 166 "y": 317
}, },
{ {
"x": 1292.2, "x": 397.4,
"y": 186 "y": 337
}, },
{ {
"x": 1229, "x": 339,
"y": 226 "y": 377
} }
], ],
"isCurve": true, "isCurve": true,
@ -358,20 +489,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1128.5, "x": 246.5,
"y": 352 "y": 503
}, },
{ {
"x": 1128.5, "x": 246.5,
"y": 392 "y": 543
}, },
{ {
"x": 1128.5, "x": 246.5,
"y": 412 "y": 563
}, },
{ {
"x": 1128.5, "x": 246.5,
"y": 452 "y": 603
} }
], ],
"isCurve": true, "isCurve": true,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 484 KiB

After

Width:  |  Height:  |  Size: 508 KiB

View file

@ -3,20 +3,20 @@
"shapes": [ "shapes": [
{ {
"id": "a", "id": "a",
"type": "code", "type": "text",
"pos": { "pos": {
"x": 12, "x": 291,
"y": 216 "y": 196
}, },
"width": 1022, "width": 154,
"height": 38, "height": 41,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "#FFFFFF", "fill": "#FFFFFF",
"stroke": "#0A0F25", "stroke": "#0D32B2",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
"multiple": false, "multiple": false,
@ -35,25 +35,25 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 1022, "labelWidth": 154,
"labelHeight": 38 "labelHeight": 41
}, },
{ {
"id": "b", "id": "b",
"type": "code", "type": "text",
"pos": { "pos": {
"x": 941, "x": 336,
"y": 158 "y": 158
}, },
"width": 93, "width": 65,
"height": 38, "height": 18,
"level": 1, "level": 1,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "#FFFFFF", "fill": "#FFFFFF",
"stroke": "#0A0F25", "stroke": "#0D32B2",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
"multiple": false, "multiple": false,
@ -72,15 +72,52 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 93, "labelWidth": 65,
"labelHeight": 38 "labelHeight": 18
},
{
"id": "z",
"type": "text",
"pos": {
"x": 12,
"y": 150
},
"width": 179,
"height": 51,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "gibberish\\\\; math:\\\\sum_{i=0}^\\\\infty i^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 179,
"labelHeight": 51
}, },
{ {
"id": "c", "id": "c",
"type": "", "type": "",
"pos": { "pos": {
"x": 1134, "x": 545,
"y": 114 "y": 104
}, },
"width": 214, "width": 214,
"height": 126, "height": 126,
@ -117,7 +154,7 @@
"id": "sugar", "id": "sugar",
"type": "", "type": "",
"pos": { "pos": {
"x": 888, "x": 299,
"y": 12 "y": 12
}, },
"width": 146, "width": 146,
@ -155,8 +192,8 @@
"id": "solution", "id": "solution",
"type": "", "type": "",
"pos": { "pos": {
"x": 1592, "x": 1003,
"y": 114 "y": 104
}, },
"width": 164, "width": 164,
"height": 126, "height": 126,
@ -191,6 +228,90 @@
} }
], ],
"connections": [ "connections": [
{
"id": "(z -> a)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "a",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 191,
"y": 184
},
{
"x": 241,
"y": 184
},
{
"x": 241,
"y": 216.5
},
{
"x": 291,
"y": 216.5
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(z -> b)[0]",
"src": "z",
"srcArrow": "none",
"srcLabel": "",
"dst": "b",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 191,
"y": 167
},
{
"x": 335.5,
"y": 167
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{ {
"id": "(a -> c)[0]", "id": "(a -> c)[0]",
"src": "a", "src": "a",
@ -217,20 +338,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1034, "x": 445,
"y": 235 "y": 216.5
}, },
{ {
"x": 1084, "x": 495,
"y": 235 "y": 216.5
}, },
{ {
"x": 1084, "x": 495,
"y": 208.5 "y": 198.5
}, },
{ {
"x": 1134, "x": 545,
"y": 208.5 "y": 198.5
} }
], ],
"animated": false, "animated": false,
@ -263,12 +384,12 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1034, "x": 400.5,
"y": 177 "y": 167
}, },
{ {
"x": 1134, "x": 545,
"y": 177 "y": 167
} }
], ],
"animated": false, "animated": false,
@ -301,20 +422,20 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1034, "x": 445,
"y": 75 "y": 75
}, },
{ {
"x": 1084, "x": 495,
"y": 75 "y": 75
}, },
{ {
"x": 1084, "x": 495,
"y": 145.5 "y": 135.5
}, },
{ {
"x": 1134, "x": 545,
"y": 145.5 "y": 135.5
} }
], ],
"animated": false, "animated": false,
@ -347,12 +468,12 @@
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 1348, "x": 759,
"y": 177 "y": 167
}, },
{ {
"x": 1592, "x": 1003,
"y": 177 "y": 167
} }
], ],
"animated": false, "animated": false,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 484 KiB

After

Width:  |  Height:  |  Size: 508 KiB

View file

@ -25,14 +25,13 @@ b: |latex
e = mc^2 e = mc^2
| |
complex: |latex z: |latex
f(x) = \\begin{dcases*} gibberish\\; math:\\sum_{i=0}^\\infty i^2
x & when $x$ is even\\\
-x & when $x$ is odd
\\end{dcases*}
| |
complex -> c z -> a
z -> b
a -> c a -> c
b -> c b -> c
sugar -> c sugar -> c