diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index a5c689214..184959f06 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,5 +1,6 @@
#### Features ๐
+- `style.font: mono` to use a monospaced font for the text/label [#1010](https://github.com/terrastruct/d2/pull/1010)
- `border-radius` is supported for both `class` and `sql_table` shapes. [#982](https://github.com/terrastruct/d2/pull/982)
#### Improvements ๐งน
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index eadf47088..670d45b93 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -252,10 +252,10 @@ func (s *Style) Apply(key, value string) error {
if s.Font == nil {
break
}
- if !go2.Contains(systemFonts, strings.ToUpper(value)) {
+ if _, ok := d2fonts.D2_FONT_TO_FAMILY[strings.ToLower(value)]; !ok {
return fmt.Errorf(`"%v" is not a valid font in our system`, value)
}
- s.Font.Value = strings.ToUpper(value)
+ s.Font.Value = strings.ToLower(value)
case "font-size":
if s.FontSize == nil {
break
@@ -793,6 +793,11 @@ func (obj *Object) AppendReferences(ida []string, ref Reference, unresolvedObj *
func (obj *Object) GetLabelSize(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily) (*d2target.TextDimensions, error) {
shapeType := strings.ToLower(obj.Attributes.Shape.Value)
+ if obj.Attributes.Style.Font != nil {
+ f := d2fonts.D2_FONT_TO_FAMILY[obj.Attributes.Style.Font.Value]
+ fontFamily = &f
+ }
+
var dims *d2target.TextDimensions
switch shapeType {
case d2target.ShapeText:
diff --git a/d2graph/font_helper.go b/d2graph/font_helper.go
deleted file mode 100644
index b8bf30523..000000000
--- a/d2graph/font_helper.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package d2graph
-
-var systemFonts = []string{
- "DEFAULT",
- "SERIOUS",
- "DIGITAL",
- "EDUCATIONAL",
- "NEWSPAPER",
- "MONO",
-}
diff --git a/d2renderers/d2fonts/d2fonts.go b/d2renderers/d2fonts/d2fonts.go
index af176ca42..d42908a13 100644
--- a/d2renderers/d2fonts/d2fonts.go
+++ b/d2renderers/d2fonts/d2fonts.go
@@ -211,3 +211,8 @@ func init() {
Style: FONT_STYLE_BOLD,
}] = b
}
+
+var D2_FONT_TO_FAMILY = map[string]FontFamily{
+ "default": SourceSansPro,
+ "mono": SourceCodePro,
+}
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 3147a6958..1e8629c86 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -558,6 +558,9 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
if connection.Label != "" {
fontClass := "text"
+ if connection.FontFamily == "mono" {
+ fontClass = "text-mono"
+ }
if connection.Bold {
fontClass += "-bold"
} else if connection.Italic {
@@ -1191,10 +1194,14 @@ func drawShape(writer io.Writer, diagramHash string, targetShape d2target.Shape,
)
fontClass := "text"
- if targetShape.Bold {
- fontClass += "-bold"
- } else if targetShape.Italic {
- fontClass += "-italic"
+ if targetShape.FontFamily == "mono" {
+ fontClass = "text-mono"
+ } else {
+ if targetShape.Bold {
+ fontClass += "-bold"
+ } else if targetShape.Italic {
+ fontClass += "-italic"
+ }
}
if targetShape.Underline {
fontClass += " text-underline"
@@ -1493,7 +1500,7 @@ func embedFonts(buf *bytes.Buffer, diagramHash, source string, fontFamily *d2fon
buf,
source,
[]string{
- `class="text-mono-bold"`,
+ `class="text-mono-bold`,
},
fmt.Sprintf(`
.%s .text-mono-bold {
@@ -1514,7 +1521,7 @@ func embedFonts(buf *bytes.Buffer, diagramHash, source string, fontFamily *d2fon
buf,
source,
[]string{
- `class="text-mono-italic"`,
+ `class="text-mono-italic`,
},
fmt.Sprintf(`
.%s .text-mono-italic {
diff --git a/e2etests-cli/testdata/TestCLI_E2E/internal_linked_pdf.exp.pdf b/e2etests-cli/testdata/TestCLI_E2E/internal_linked_pdf.exp.pdf
index 9d14d8f5d..d08f7f671 100644
Binary files a/e2etests-cli/testdata/TestCLI_E2E/internal_linked_pdf.exp.pdf and b/e2etests-cli/testdata/TestCLI_E2E/internal_linked_pdf.exp.pdf differ
diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go
index 60ec154ac..706225b51 100644
--- a/e2etests/stable_test.go
+++ b/e2etests/stable_test.go
@@ -22,7 +22,7 @@ func testStable(t *testing.T) {
json: jsonb {constraint: unique}
last_updated: timestamp with time zone
-
+
style: {
fill: red
border-radius: 10
@@ -34,7 +34,7 @@ func testStable(t *testing.T) {
field: "[]string"
method(a uint64): (x, y int)
-
+
style: {
border-radius: 10
}
@@ -81,6 +81,40 @@ func testStable(t *testing.T) {
}
`,
},
+ {
+ name: "mono-font",
+ script: `satellites: SATELLITES {
+ shape: stored_data
+ style: {
+ font: mono
+ fill: white
+ stroke: black
+ multiple: true
+ }
+}
+
+transmitter: TRANSMITTER {
+ style: {
+ font: mono
+ fill: white
+ stroke: black
+ }
+}
+
+satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+}
+satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+}
+satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+}
+`,
+ },
{
name: "connected_container",
script: `a.b -> c.d -> f.h.g
diff --git a/e2etests/testdata/stable/mono-font/dagre/board.exp.json b/e2etests/testdata/stable/mono-font/dagre/board.exp.json
new file mode 100644
index 000000000..00d4c9ddc
--- /dev/null
+++ b/e2etests/testdata/stable/mono-font/dagre/board.exp.json
@@ -0,0 +1,278 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "satellites",
+ "type": "stored_data",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 161,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "white",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "SATELLITES",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "transmitter",
+ "type": "rectangle",
+ "pos": {
+ "x": 5,
+ "y": 187
+ },
+ "width": 151,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "white",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "TRANSMITTER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 106,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(satellites -> transmitter)[0]",
+ "src": "satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 60,
+ "y": 66
+ },
+ {
+ "x": 30.799999999999997,
+ "y": 114.4
+ },
+ {
+ "x": 30.9,
+ "y": 138.7
+ },
+ {
+ "x": 60.5,
+ "y": 187.5
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(satellites -> transmitter)[1]",
+ "src": "satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 80,
+ "y": 66
+ },
+ {
+ "x": 80.4,
+ "y": 114.4
+ },
+ {
+ "x": 80.5,
+ "y": 138.7
+ },
+ {
+ "x": 80.5,
+ "y": 187.5
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(satellites -> transmitter)[2]",
+ "src": "satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 101,
+ "y": 66
+ },
+ {
+ "x": 130.2,
+ "y": 114.4
+ },
+ {
+ "x": 130.1,
+ "y": 138.7
+ },
+ {
+ "x": 100.5,
+ "y": 187.5
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "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/stable/mono-font/dagre/sketch.exp.svg b/e2etests/testdata/stable/mono-font/dagre/sketch.exp.svg
new file mode 100644
index 000000000..d2f51fb93
--- /dev/null
+++ b/e2etests/testdata/stable/mono-font/dagre/sketch.exp.svg
@@ -0,0 +1,104 @@
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/mono-font/elk/board.exp.json b/e2etests/testdata/stable/mono-font/elk/board.exp.json
new file mode 100644
index 000000000..508431842
--- /dev/null
+++ b/e2etests/testdata/stable/mono-font/elk/board.exp.json
@@ -0,0 +1,283 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "satellites",
+ "type": "stored_data",
+ "pos": {
+ "x": 12,
+ "y": 12
+ },
+ "width": 161,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "white",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "SATELLITES",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "transmitter",
+ "type": "rectangle",
+ "pos": {
+ "x": 24,
+ "y": 259
+ },
+ "width": 151,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "white",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "TRANSMITTER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 106,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(satellites -> transmitter)[0]",
+ "src": "satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 52,
+ "y": 78
+ },
+ {
+ "x": 52.25,
+ "y": 219
+ },
+ {
+ "x": 62.5,
+ "y": 219
+ },
+ {
+ "x": 62.5,
+ "y": 259
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(satellites -> transmitter)[1]",
+ "src": "satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 92,
+ "y": 78
+ },
+ {
+ "x": 92.5,
+ "y": 118
+ },
+ {
+ "x": 100.25,
+ "y": 118
+ },
+ {
+ "x": 100.25,
+ "y": 259
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(satellites -> transmitter)[2]",
+ "src": "satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 133,
+ "y": 78
+ },
+ {
+ "x": 132.75,
+ "y": 118
+ },
+ {
+ "x": 148.25,
+ "y": 118
+ },
+ {
+ "x": 148.25,
+ "y": 219
+ },
+ {
+ "x": 138,
+ "y": 219
+ },
+ {
+ "x": 138,
+ "y": 259
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "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/stable/mono-font/elk/sketch.exp.svg b/e2etests/testdata/stable/mono-font/elk/sketch.exp.svg
new file mode 100644
index 000000000..666044ea9
--- /dev/null
+++ b/e2etests/testdata/stable/mono-font/elk/sketch.exp.svg
@@ -0,0 +1,104 @@
+SATELLITESTRANSMITTER SENDSENDSEND
+
+
+
+
+
\ No newline at end of file