diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index 7e2eff173..58be7d176 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -3,5 +3,7 @@
#### Improvements 🧹
#### Bugfixes ⛑️
+
- Accept absolute paths again on the CLI. [#979](https://github.com/terrastruct/d2/pull/979)
- Fixes some rare undefined behavior using capitalized reserved keywords [#978](https://github.com/terrastruct/d2/pull/978)
+- Fixes an error rendering when links contained `&` characters [#988](https://github.com/terrastruct/d2/pull/988)
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index afe1acd8d..2846c7587 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -868,7 +868,8 @@ func render3dHexagon(targetShape d2target.Shape) string {
func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2sketch.Runner) (labelMask string, err error) {
closingTag := ""
if targetShape.Link != "" {
- fmt.Fprintf(writer, ``, targetShape.Link)
+
+ fmt.Fprintf(writer, ``, svg.EscapeText(targetShape.Link))
closingTag += ""
}
// Opacity is a unique style, it applies to everything for a shape
diff --git a/e2etests/regression_test.go b/e2etests/regression_test.go
index 38ca7ce97..1d47c95e6 100644
--- a/e2etests/regression_test.go
+++ b/e2etests/regression_test.go
@@ -642,6 +642,10 @@ l3c1.a -> l4.c1.a
l3c1.b -> l4.c2.b
l3c2.c -> l4.c3.c`,
},
+ {
+ name: "link_with_ampersand",
+ script: `a.link: https://calendar.google.com/calendar/u/0/r?tab=mc&pli=1`,
+ },
}
runa(t, tcs)
diff --git a/e2etests/testdata/regression/link_with_ampersand/dagre/board.exp.json b/e2etests/testdata/regression/link_with_ampersand/dagre/board.exp.json
new file mode 100644
index 000000000..0667ba543
--- /dev/null
+++ b/e2etests/testdata/regression/link_with_ampersand/dagre/board.exp.json
@@ -0,0 +1,89 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "rectangle",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 85,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "https://calendar.google.com/calendar/u/0/r?tab=mc&pli=1",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/regression/link_with_ampersand/dagre/sketch.exp.svg b/e2etests/testdata/regression/link_with_ampersand/dagre/sketch.exp.svg
new file mode 100644
index 000000000..6b417a12d
--- /dev/null
+++ b/e2etests/testdata/regression/link_with_ampersand/dagre/sketch.exp.svg
@@ -0,0 +1,38 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/regression/link_with_ampersand/elk/board.exp.json b/e2etests/testdata/regression/link_with_ampersand/elk/board.exp.json
new file mode 100644
index 000000000..6ef099dc8
--- /dev/null
+++ b/e2etests/testdata/regression/link_with_ampersand/elk/board.exp.json
@@ -0,0 +1,89 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "rectangle",
+ "pos": {
+ "x": 12,
+ "y": 12
+ },
+ "width": 85,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "https://calendar.google.com/calendar/u/0/r?tab=mc&pli=1",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 8,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/regression/link_with_ampersand/elk/sketch.exp.svg b/e2etests/testdata/regression/link_with_ampersand/elk/sketch.exp.svg
new file mode 100644
index 000000000..a5d3845bb
--- /dev/null
+++ b/e2etests/testdata/regression/link_with_ampersand/elk/sketch.exp.svg
@@ -0,0 +1,38 @@
+a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file