2022-11-27 10:11:14AM

This commit is contained in:
Alexander Wang 2022-11-27 10:11:14 -08:00
parent b90ca0a166
commit fc4cea1a89
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
7 changed files with 791 additions and 1592 deletions

View file

@ -6,12 +6,18 @@ import (
)
func TestSVG(t *testing.T) {
svg, err := SVG("$$a + b = c$$")
if err != nil {
t.Fatal(err)
txts := []string{
"a + b = c",
"\\\\frac{1}{2}",
}
var xmlParsed interface{}
if err := xml.Unmarshal([]byte(svg), &xmlParsed); err != nil {
t.Fatalf("invalid SVG: %v", err)
for _, txt := range txts {
svg, err := SVG(txt)
if err != nil {
t.Fatal(err)
}
var xmlParsed interface{}
if err := xml.Unmarshal([]byte(svg), &xmlParsed); err != nil {
t.Fatalf("invalid SVG: %v", err)
}
}
}

View file

@ -18,8 +18,10 @@ import (
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
"github.com/davecgh/go-spew/spew"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
"oss.terrastruct.com/d2/d2renderers/textmeasure"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/lib/color"
@ -660,46 +662,60 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error {
switch targetShape.Type {
case d2target.ShapeCode:
lexer := lexers.Get(targetShape.Language)
if lexer == nil {
return fmt.Errorf("code snippet lexer for %s not found", targetShape.Language)
}
style := styles.Get("github")
if style == nil {
return errors.New(`code snippet style "github" not found`)
}
formatter := formatters.Get("svg")
if formatter == nil {
return errors.New(`code snippet formatter "svg" not found`)
}
iterator, err := lexer.Tokenise(nil, targetShape.Label)
if err != nil {
return err
}
svgStyles := styleToSVG(style)
containerStyle := fmt.Sprintf(`stroke: %s;fill:%s`, targetShape.Stroke, style.Get(chroma.Background).Background.String())
fmt.Fprintf(writer, `<g transform="translate(%f %f)" style="opacity:%f">`, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
fmt.Fprintf(writer, `<rect class="shape" width="%d" height="%d" style="%s" />`,
targetShape.Width, targetShape.Height, containerStyle)
// Padding
fmt.Fprintf(writer, `<g transform="translate(6 6)">`)
for index, tokens := range chroma.SplitTokensIntoLines(iterator.Tokens()) {
// TODO mono font looks better with 1.2 em (use px equivalent), but textmeasure needs to account for it. Not obvious how that should be done
fmt.Fprintf(writer, "<text class=\"text-mono\" x=\"0\" y=\"%fem\" xml:space=\"preserve\">", 1*float64(index+1))
for _, token := range tokens {
text := svgEscaper.Replace(token.String())
attr := styleAttr(svgStyles, token.Type)
if attr != "" {
text = fmt.Sprintf("<tspan %s>%s</tspan>", attr, text)
}
fmt.Fprint(writer, text)
if targetShape.Language == "latex" {
spew.Dump(targetShape.Label)
render, err := d2latex.SVG(targetShape.Label)
if err != nil {
spew.Dump(err)
return err
}
fmt.Fprint(writer, "</text>")
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)
if lexer == nil {
return fmt.Errorf("code snippet lexer for %s not found", targetShape.Language)
}
style := styles.Get("github")
if style == nil {
return errors.New(`code snippet style "github" not found`)
}
formatter := formatters.Get("svg")
if formatter == nil {
return errors.New(`code snippet formatter "svg" not found`)
}
iterator, err := lexer.Tokenise(nil, targetShape.Label)
if err != nil {
return err
}
svgStyles := styleToSVG(style)
containerStyle := fmt.Sprintf(`stroke: %s;fill:%s`, targetShape.Stroke, style.Get(chroma.Background).Background.String())
fmt.Fprintf(writer, `<g transform="translate(%f %f)" style="opacity:%f">`, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
fmt.Fprintf(writer, `<rect class="shape" width="%d" height="%d" style="%s" />`,
targetShape.Width, targetShape.Height, containerStyle)
// Padding
fmt.Fprintf(writer, `<g transform="translate(6 6)">`)
for index, tokens := range chroma.SplitTokensIntoLines(iterator.Tokens()) {
// TODO mono font looks better with 1.2 em (use px equivalent), but textmeasure needs to account for it. Not obvious how that should be done
fmt.Fprintf(writer, "<text class=\"text-mono\" x=\"0\" y=\"%fem\" xml:space=\"preserve\">", 1*float64(index+1))
for _, token := range tokens {
text := svgEscaper.Replace(token.String())
attr := styleAttr(svgStyles, token.Type)
if attr != "" {
text = fmt.Sprintf("<tspan %s>%s</tspan>", attr, text)
}
fmt.Fprint(writer, text)
}
fmt.Fprint(writer, "</text>")
}
fmt.Fprintf(writer, "</g></g>")
}
fmt.Fprintf(writer, "</g></g>")
case d2target.ShapeText:
render, err := textmeasure.RenderMarkdown(targetShape.Label)
if err != nil {

View file

@ -2,20 +2,94 @@
"name": "",
"shapes": [
{
"id": "hi",
"type": "text",
"id": "a",
"type": "code",
"pos": {
"x": 0,
"y": 0
"y": 44
},
"width": 167,
"height": 24,
"width": 1022,
"height": 38,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0A0F25",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "\\\\frac{\\\\alpha g^2}{\\\\omega^5} e^{[ -0.74\\\\bigl\\\\{\\\\frac{\\\\omega U_\\\\omega 19.5}{g}\\\\bigr\\\\}^{\\\\!-4}\\\\,]}",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 1022,
"labelHeight": 38
},
{
"id": "b",
"type": "code",
"pos": {
"x": 1082,
"y": 44
},
"width": 93,
"height": 38,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0A0F25",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "e = mc^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 93,
"labelHeight": 38
},
{
"id": "c",
"type": "",
"pos": {
"x": 1022,
"y": 226
},
"width": 214,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
@ -27,17 +101,283 @@
"fields": null,
"methods": null,
"columns": null,
"label": "Inline math $\\frac{1}{2}$",
"label": "mixed together",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "markdown",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 167,
"labelHeight": 24
"labelWidth": 114,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "sugar",
"type": "",
"pos": {
"x": 1235,
"y": 0
},
"width": 146,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "sugar",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 46,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "solution",
"type": "",
"pos": {
"x": 1047,
"y": 452
},
"width": 164,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "solution",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 64,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
}
],
"connections": []
"connections": [
{
"id": "(a -> c)[0]",
"src": "a",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"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": 511,
"y": 82
},
{
"x": 511,
"y": 157.2
},
{
"x": 613.1,
"y": 194.6838866396761
},
{
"x": 1021.5,
"y": 269.41943319838055
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(b -> c)[0]",
"src": "b",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"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": 1128.5,
"y": 82
},
{
"x": 1128.5,
"y": 157.2
},
{
"x": 1128.5,
"y": 186
},
{
"x": 1128.5,
"y": 226
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(sugar -> c)[0]",
"src": "sugar",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"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": 1308,
"y": 126
},
{
"x": 1308,
"y": 166
},
{
"x": 1292.2,
"y": 186
},
{
"x": 1229,
"y": 226
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(c -> solution)[0]",
"src": "c",
"srcArrow": "none",
"srcLabel": "",
"dst": "solution",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "we get",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 1128.5,
"y": 352
},
{
"x": 1128.5,
"y": 392
},
{
"x": 1128.5,
"y": 412
},
{
"x": 1128.5,
"y": 452
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null
}
]
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 484 KiB

View file

@ -2,20 +2,94 @@
"name": "",
"shapes": [
{
"id": "hi",
"type": "text",
"id": "a",
"type": "code",
"pos": {
"x": 12,
"y": 12
"y": 216
},
"width": 167,
"height": 24,
"width": 1022,
"height": 38,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0A0F25",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "\\\\frac{\\\\alpha g^2}{\\\\omega^5} e^{[ -0.74\\\\bigl\\\\{\\\\frac{\\\\omega U_\\\\omega 19.5}{g}\\\\bigr\\\\}^{\\\\!-4}\\\\,]}",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 1022,
"labelHeight": 38
},
{
"id": "b",
"type": "code",
"pos": {
"x": 941,
"y": 158
},
"width": 93,
"height": 38,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#FFFFFF",
"stroke": "#0A0F25",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "e = mc^2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "latex",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 93,
"labelHeight": 38
},
{
"id": "c",
"type": "",
"pos": {
"x": 1134,
"y": 114
},
"width": 214,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
@ -27,17 +101,263 @@
"fields": null,
"methods": null,
"columns": null,
"label": "Inline math $\\frac{1}{2}$",
"label": "mixed together",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "markdown",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 167,
"labelHeight": 24
"labelWidth": 114,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "sugar",
"type": "",
"pos": {
"x": 888,
"y": 12
},
"width": 146,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "sugar",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 46,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
},
{
"id": "solution",
"type": "",
"pos": {
"x": 1592,
"y": 114
},
"width": 164,
"height": 126,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "solution",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 64,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER"
}
],
"connections": []
"connections": [
{
"id": "(a -> c)[0]",
"src": "a",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"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": 1034,
"y": 235
},
{
"x": 1084,
"y": 235
},
{
"x": 1084,
"y": 208.5
},
{
"x": 1134,
"y": 208.5
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(b -> c)[0]",
"src": "b",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"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": 1034,
"y": 177
},
{
"x": 1134,
"y": 177
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(sugar -> c)[0]",
"src": "sugar",
"srcArrow": "none",
"srcLabel": "",
"dst": "c",
"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": 1034,
"y": 75
},
{
"x": 1084,
"y": 75
},
{
"x": 1084,
"y": 145.5
},
{
"x": 1134,
"y": 145.5
}
],
"animated": false,
"tooltip": "",
"icon": null
},
{
"id": "(c -> solution)[0]",
"src": "c",
"srcArrow": "none",
"srcLabel": "",
"dst": "solution",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "we get",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 1348,
"y": 177
},
{
"x": 1592,
"y": 177
}
],
"animated": false,
"tooltip": "",
"icon": null
}
]
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 484 KiB

View file

@ -17,9 +17,20 @@ container -> container.second: c->2
},
{
name: "latex",
script: `hi: |md
Inline math $\frac{1}{2}$
script: `a: |latex
\\frac{\\alpha g^2}{\\omega^5} e^{[ -0.74\\bigl\\{\\frac{\\omega U_\\omega 19.5}{g}\\bigr\\}^{\\!-4}\\,]}
|
b: |latex
e = mc^2
|
a -> c
b -> c
sugar -> c
c: mixed together
c -> solution: we get
`,
},
}