diff --git a/d2exporter/export.go b/d2exporter/export.go
index 1eb2e6445..9706365f0 100644
--- a/d2exporter/export.go
+++ b/d2exporter/export.go
@@ -81,19 +81,17 @@ func applyStyles(shape *d2target.Shape, obj *d2graph.Object) {
if obj.Attributes.Style.FontColor != nil {
shape.Color = obj.Attributes.Style.FontColor.Value
}
- if obj.Attributes.Shape.Value != d2target.ShapeText {
- if obj.Attributes.Style.Italic != nil {
- shape.Italic, _ = strconv.ParseBool(obj.Attributes.Style.Italic.Value)
- }
- if obj.Attributes.Style.Bold != nil {
- shape.Bold, _ = strconv.ParseBool(obj.Attributes.Style.Bold.Value)
- }
- if obj.Attributes.Style.Underline != nil {
- shape.Underline, _ = strconv.ParseBool(obj.Attributes.Style.Underline.Value)
- }
- if obj.Attributes.Style.Font != nil {
- shape.FontFamily = obj.Attributes.Style.Font.Value
- }
+ if obj.Attributes.Style.Italic != nil {
+ shape.Italic, _ = strconv.ParseBool(obj.Attributes.Style.Italic.Value)
+ }
+ if obj.Attributes.Style.Bold != nil {
+ shape.Bold, _ = strconv.ParseBool(obj.Attributes.Style.Bold.Value)
+ }
+ if obj.Attributes.Style.Underline != nil {
+ shape.Underline, _ = strconv.ParseBool(obj.Attributes.Style.Underline.Value)
+ }
+ if obj.Attributes.Style.Font != nil {
+ shape.FontFamily = obj.Attributes.Style.Font.Value
}
}
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index ebaf0274c..da137cc12 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -441,7 +441,14 @@ func (obj *Object) AbsIDArray() []string {
}
func (obj *Object) Text() *d2target.MText {
- isBold := !obj.IsContainer()
+ isBold := !obj.IsContainer() && obj.Attributes.Shape.Value != "text"
+ isItalic := false
+ if obj.Attributes.Style.Bold != nil && obj.Attributes.Style.Bold.Value == "true" {
+ isBold = true
+ }
+ if obj.Attributes.Style.Italic != nil && obj.Attributes.Style.Italic.Value == "true" {
+ isItalic = true
+ }
fontSize := d2fonts.FONT_SIZE_M
if obj.OuterSequenceDiagram() == nil {
if obj.IsContainer() {
@@ -464,7 +471,7 @@ func (obj *Object) Text() *d2target.MText {
Text: obj.Attributes.Label.Value,
FontSize: fontSize,
IsBold: isBold,
- IsItalic: false,
+ IsItalic: isItalic,
Language: obj.Attributes.Language,
Shape: obj.Attributes.Shape.Value,
@@ -908,12 +915,14 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
return err
}
dims = d2target.NewTextDimensions(width, height)
- } else {
+ } else if obj.Attributes.Language != "" {
var err error
dims, err = getMarkdownDimensions(mtexts, ruler, obj.Text(), fontFamily)
if err != nil {
return err
}
+ } else {
+ dims = getTextDimensions(mtexts, ruler, obj.Text(), fontFamily)
}
innerLabelPadding = 0
} else if obj.Attributes.Shape.Value == d2target.ShapeClass {
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index c237483e4..90e7bb191 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -741,9 +741,11 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske
} else if targetShape.Italic {
fontClass += "-italic"
}
+ if targetShape.Underline {
+ fontClass += " text-underline"
+ }
- switch targetShape.Type {
- case d2target.ShapeCode:
+ if targetShape.Type == d2target.ShapeCode {
lexer := lexers.Get(targetShape.Language)
if lexer == nil {
return labelMask, fmt.Errorf("code snippet lexer for %s not found", targetShape.Language)
@@ -784,38 +786,36 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske
fmt.Fprint(writer, "")
}
fmt.Fprintf(writer, "")
- case d2target.ShapeText:
- if targetShape.Language == "latex" {
- render, err := d2latex.Render(targetShape.Label)
- if err != nil {
- return labelMask, err
- }
- fmt.Fprintf(writer, ``, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
- fmt.Fprint(writer, render)
- fmt.Fprintf(writer, "")
- } else {
- render, err := textmeasure.RenderMarkdown(targetShape.Label)
- if err != nil {
- return labelMask, err
- }
- fmt.Fprintf(writer, ``,
- box.TopLeft.X, box.TopLeft.Y, targetShape.Width, targetShape.Height,
- )
- // we need the self closing form in this svg/xhtml context
- render = strings.ReplaceAll(render, "
", "
")
-
- var mdStyle string
- if targetShape.Fill != "" {
- mdStyle = fmt.Sprintf("background-color:%s;", targetShape.Fill)
- }
- if targetShape.Stroke != "" {
- mdStyle += fmt.Sprintf("color:%s;", targetShape.Stroke)
- }
-
- fmt.Fprintf(writer, `%v
`, mdStyle, render)
- fmt.Fprint(writer, ``)
+ } else if targetShape.Type == d2target.ShapeText && targetShape.Language == "latex" {
+ render, err := d2latex.Render(targetShape.Label)
+ if err != nil {
+ return labelMask, err
}
- default:
+ fmt.Fprintf(writer, ``, box.TopLeft.X, box.TopLeft.Y, targetShape.Opacity)
+ fmt.Fprint(writer, render)
+ fmt.Fprintf(writer, "")
+ } else if targetShape.Type == d2target.ShapeText && targetShape.Language != "" {
+ render, err := textmeasure.RenderMarkdown(targetShape.Label)
+ if err != nil {
+ return labelMask, err
+ }
+ fmt.Fprintf(writer, ``,
+ box.TopLeft.X, box.TopLeft.Y, targetShape.Width, targetShape.Height,
+ )
+ // we need the self closing form in this svg/xhtml context
+ render = strings.ReplaceAll(render, "
", "
")
+
+ var mdStyle string
+ if targetShape.Fill != "" {
+ mdStyle = fmt.Sprintf("background-color:%s;", targetShape.Fill)
+ }
+ if targetShape.Stroke != "" {
+ mdStyle += fmt.Sprintf("color:%s;", targetShape.Stroke)
+ }
+
+ fmt.Fprintf(writer, `%v
`, mdStyle, render)
+ fmt.Fprint(writer, ``)
+ } else {
fontColor := "black"
if targetShape.Color != "" {
fontColor = targetShape.Color
@@ -903,6 +903,7 @@ func embedFonts(buf *bytes.Buffer, fontFamily *d2fonts.FontFamily) {
triggers := []string{
`class="text"`,
+ `class="text `,
`class="md"`,
}
@@ -921,6 +922,20 @@ func embedFonts(buf *bytes.Buffer, fontFamily *d2fonts.FontFamily) {
}
}
+ triggers = []string{
+ `text-underline`,
+ }
+
+ for _, t := range triggers {
+ if strings.Contains(content, t) {
+ buf.WriteString(`
+.text-underline {
+ text-decoration: underline;
+}`)
+ break
+ }
+ }
+
triggers = []string{
`class="text-bold"`,
``,
diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go
index 6a95949be..2df367f50 100644
--- a/e2etests/stable_test.go
+++ b/e2etests/stable_test.go
@@ -1615,6 +1615,15 @@ i am bottom right: { shape: text; near: bottom-right }
poll the people -> results
results -> unfavorable -> poll the people
results -> favorable -> will of the people
+`,
+ },
+ {
+ name: "text_font_sizes",
+ script: `bear: { shape: text; style.font-size: 22; style.bold: true }
+mama bear: { shape: text; style.font-size: 28; style.italic: true }
+papa bear: { shape: text; style.font-size: 32; style.underline: true }
+mama bear -> bear
+papa bear -> bear
`,
},
}
diff --git a/e2etests/testdata/stable/text_font_sizes/dagre/board.exp.json b/e2etests/testdata/stable/text_font_sizes/dagre/board.exp.json
new file mode 100644
index 000000000..fdb475c2b
--- /dev/null
+++ b/e2etests/testdata/stable/text_font_sizes/dagre/board.exp.json
@@ -0,0 +1,221 @@
+{
+ "name": "",
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "bear",
+ "type": "text",
+ "pos": {
+ "x": 143,
+ "y": 141
+ },
+ "width": 44,
+ "height": 28,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "transparent",
+ "stroke": "#0A0F25",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "bear",
+ "fontSize": 22,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 44,
+ "labelHeight": 28,
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "mama bear",
+ "type": "text",
+ "pos": {
+ "x": 0,
+ "y": 3
+ },
+ "width": 135,
+ "height": 36,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "transparent",
+ "stroke": "#0A0F25",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "mama bear",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 135,
+ "labelHeight": 36,
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "papa bear",
+ "type": "text",
+ "pos": {
+ "x": 195,
+ "y": 0
+ },
+ "width": 134,
+ "height": 41,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "transparent",
+ "stroke": "#0A0F25",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "papa bear",
+ "fontSize": 32,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": false,
+ "underline": true,
+ "labelWidth": 134,
+ "labelHeight": 41,
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(mama bear -> bear)[0]",
+ "src": "mama bear",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "bear",
+ "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": 67.5,
+ "y": 39.5
+ },
+ {
+ "x": 67.5,
+ "y": 80.7
+ },
+ {
+ "x": 82.7,
+ "y": 101
+ },
+ {
+ "x": 143.5,
+ "y": 141
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(papa bear -> bear)[0]",
+ "src": "papa bear",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "bear",
+ "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": 262,
+ "y": 41
+ },
+ {
+ "x": 262,
+ "y": 81
+ },
+ {
+ "x": 246.8,
+ "y": 101
+ },
+ {
+ "x": 186,
+ "y": 141
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ]
+}
diff --git a/e2etests/testdata/stable/text_font_sizes/dagre/sketch.exp.svg b/e2etests/testdata/stable/text_font_sizes/dagre/sketch.exp.svg
new file mode 100644
index 000000000..5c31b0803
--- /dev/null
+++ b/e2etests/testdata/stable/text_font_sizes/dagre/sketch.exp.svg
@@ -0,0 +1,805 @@
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/text_font_sizes/elk/board.exp.json b/e2etests/testdata/stable/text_font_sizes/elk/board.exp.json
new file mode 100644
index 000000000..ae4e7c84e
--- /dev/null
+++ b/e2etests/testdata/stable/text_font_sizes/elk/board.exp.json
@@ -0,0 +1,211 @@
+{
+ "name": "",
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "bear",
+ "type": "text",
+ "pos": {
+ "x": 64,
+ "y": 153
+ },
+ "width": 44,
+ "height": 28,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "transparent",
+ "stroke": "#0A0F25",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "bear",
+ "fontSize": 22,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 44,
+ "labelHeight": 28,
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "mama bear",
+ "type": "text",
+ "pos": {
+ "x": 12,
+ "y": 17
+ },
+ "width": 135,
+ "height": 36,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "transparent",
+ "stroke": "#0A0F25",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "mama bear",
+ "fontSize": 28,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 135,
+ "labelHeight": 36,
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "papa bear",
+ "type": "text",
+ "pos": {
+ "x": 167,
+ "y": 12
+ },
+ "width": 134,
+ "height": 41,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "transparent",
+ "stroke": "#0A0F25",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "papa bear",
+ "fontSize": 32,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": false,
+ "underline": true,
+ "labelWidth": 134,
+ "labelHeight": 41,
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(mama bear -> bear)[0]",
+ "src": "mama bear",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "bear",
+ "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": 79.5,
+ "y": 53
+ },
+ {
+ "x": 79.5,
+ "y": 153
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(papa bear -> bear)[0]",
+ "src": "papa bear",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "bear",
+ "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": 234,
+ "y": 53
+ },
+ {
+ "x": 234,
+ "y": 103
+ },
+ {
+ "x": 94.16666666666666,
+ "y": 103
+ },
+ {
+ "x": 94.16666666666666,
+ "y": 153
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ]
+}
diff --git a/e2etests/testdata/stable/text_font_sizes/elk/sketch.exp.svg b/e2etests/testdata/stable/text_font_sizes/elk/sketch.exp.svg
new file mode 100644
index 000000000..506609593
--- /dev/null
+++ b/e2etests/testdata/stable/text_font_sizes/elk/sketch.exp.svg
@@ -0,0 +1,805 @@
+
+
\ No newline at end of file