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 @@ + +bearmama bearpapa bear + + + \ 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 @@ + +bearmama bearpapa bear + + + \ No newline at end of file