diff --git a/d2exporter/export.go b/d2exporter/export.go index fb820b782..c145063d6 100644 --- a/d2exporter/export.go +++ b/d2exporter/export.go @@ -166,7 +166,7 @@ func toShape(obj *d2graph.Object, g *d2graph.Graph) d2target.Shape { shape.Color = text.GetColor(shape.Italic) applyStyles(shape, obj) - switch obj.Shape.Value { + switch strings.ToLower(obj.Shape.Value) { case d2target.ShapeCode, d2target.ShapeText: shape.Language = obj.Language shape.Label = obj.Label.Value @@ -181,6 +181,10 @@ func toShape(obj *d2graph.Object, g *d2graph.Graph) d2target.Shape { if obj.ContentAspectRatio != nil { shape.ContentAspectRatio = go2.Pointer(*obj.ContentAspectRatio) } + case d2target.ShapePage: + if obj.IsSequenceDiagramNote() { + shape.Language = obj.Language + } } shape.Label = text.Text shape.LabelWidth = text.Dimensions.Width diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 52848bc1a..8bab031a0 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -583,8 +583,7 @@ func (obj *Object) GetFill() string { func (obj *Object) GetStroke(dashGapSize interface{}) string { shape := obj.Shape.Value - if strings.EqualFold(shape, d2target.ShapeCode) || - strings.EqualFold(shape, d2target.ShapeText) { + if obj.Language != "" { return color.N1 } if strings.EqualFold(shape, d2target.ShapeClass) || diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 153b7b345..ecfce033d 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -1414,7 +1414,49 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape fontClass += " text-underline" } - if targetShape.Type == d2target.ShapeCode { + if targetShape.Language == "latex" { + render, err := d2latex.Render(targetShape.Label) + if err != nil { + return labelMask, err + } + gEl := d2themes.NewThemableElement("g", inlineTheme) + gEl.SetTranslate(float64(box.TopLeft.X), float64(box.TopLeft.Y)) + gEl.Color = targetShape.Stroke + gEl.Content = render + fmt.Fprint(writer, gEl.Render()) + } else if targetShape.Language == "markdown" { + 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, "", "") + + mdEl := d2themes.NewThemableElement("div") + mdEl.ClassName = "md" + mdEl.Content = render + + // We have to set with styles since within foreignObject, we're in html + // land and not SVG attributes + var styles []string + if targetShape.FontSize != textmeasure.MarkdownFontSize { + styles = append(styles, fmt.Sprintf("font-size:%vpx", targetShape.FontSize)) + } + if targetShape.Fill != "" && targetShape.Fill != "transparent" { + styles = append(styles, fmt.Sprintf(`background-color:%s`, targetShape.Fill)) + } + if !color.IsThemeColor(targetShape.Color) { + styles = append(styles, fmt.Sprintf(`color:%s`, targetShape.Color)) + } + + mdEl.Style = strings.Join(styles, ";") + + fmt.Fprint(writer, mdEl.Render()) + fmt.Fprint(writer, ``) + } else if targetShape.Language != "" { lexer := lexers.Get(targetShape.Language) if lexer == nil { lexer = lexers.Fallback @@ -1478,48 +1520,6 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape } fmt.Fprint(writer, "") } - } else if targetShape.Type == d2target.ShapeText && targetShape.Language == "latex" { - render, err := d2latex.Render(targetShape.Label) - if err != nil { - return labelMask, err - } - gEl := d2themes.NewThemableElement("g", inlineTheme) - gEl.SetTranslate(float64(box.TopLeft.X), float64(box.TopLeft.Y)) - gEl.Color = targetShape.Stroke - gEl.Content = render - fmt.Fprint(writer, gEl.Render()) - } 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, "", "") - - mdEl := d2themes.NewThemableElement("div", inlineTheme) - mdEl.ClassName = "md" - mdEl.Content = render - - // We have to set with styles since within foreignObject, we're in html - // land and not SVG attributes - var styles []string - if targetShape.FontSize != textmeasure.MarkdownFontSize { - styles = append(styles, fmt.Sprintf("font-size:%vpx", targetShape.FontSize)) - } - if targetShape.Fill != "" && targetShape.Fill != "transparent" { - styles = append(styles, fmt.Sprintf(`background-color:%s`, targetShape.Fill)) - } - if !color.IsThemeColor(targetShape.Color) { - styles = append(styles, fmt.Sprintf(`color:%s`, targetShape.Color)) - } - - mdEl.Style = strings.Join(styles, ";") - - fmt.Fprint(writer, mdEl.Render()) - fmt.Fprint(writer, ``) } else { if targetShape.LabelFill != "" { rectEl := d2themes.NewThemableElement("rect", inlineTheme) @@ -2075,7 +2075,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { hasMarkdown := false for _, s := range diagram.Shapes { - if s.Label != "" && s.Type == d2target.ShapeText { + if s.Language == "markdown" { hasMarkdown = true break } diff --git a/e2etests/testdata/txtar/sequence-diagram-note-md/dagre/board.exp.json b/e2etests/testdata/txtar/sequence-diagram-note-md/dagre/board.exp.json new file mode 100644 index 000000000..1587ed836 --- /dev/null +++ b/e2etests/testdata/txtar/sequence-diagram-note-md/dagre/board.exp.json @@ -0,0 +1,368 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "x", + "type": "rectangle", + "pos": { + "x": 12, + "y": 52 + }, + "width": 100, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "x", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 7, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y", + "type": "rectangle", + "pos": { + "x": 242, + "y": 52 + }, + "width": 100, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "y", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "x.x", + "type": "page", + "pos": { + "x": -128, + "y": 258 + }, + "width": 380, + "height": 119, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "## A man who fishes for marlin in ponds\n\n- ...dramatic pause\n\nwill put his money in Etruscan bonds.", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 380, + "labelHeight": 119, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 5, + "level": 2 + }, + { + "id": "y.z", + "type": "page", + "pos": { + "x": 211, + "y": 447 + }, + "width": 162, + "height": 41, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "\\\\lim_{h \\\\rightarrow 0 } \\\\frac{f(x+h)-f(x)}{h}", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "latex", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 162, + "labelHeight": 41, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 5, + "level": 2 + }, + { + "id": "x.z", + "type": "page", + "pos": { + "x": 10, + "y": 558 + }, + "width": 103, + "height": 37, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "1 + 1 = 2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "python", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 87, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 5, + "level": 2 + } + ], + "connections": [ + { + "id": "(x -> y)[0]", + "src": "x", + "srcArrow": "none", + "dst": "y", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "hello", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 33, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 62, + "y": 188 + }, + { + "x": 292, + "y": 188 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 4 + }, + { + "id": "(x -- )[0]", + "src": "x", + "srcArrow": "none", + "dst": "x-lifeline-end-1678191278", + "dstArrow": "none", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "B2", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 62, + "y": 118 + }, + { + "x": 62, + "y": 665 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 1 + }, + { + "id": "(y -- )[0]", + "src": "y", + "srcArrow": "none", + "dst": "y-lifeline-end-35261543", + "dstArrow": "none", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "B2", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 292, + "y": 118 + }, + { + "x": 292, + "y": 665 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 1 + } + ], + "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/txtar/sequence-diagram-note-md/dagre/sketch.exp.svg b/e2etests/testdata/txtar/sequence-diagram-note-md/dagre/sketch.exp.svg new file mode 100644 index 000000000..d87897842 --- /dev/null +++ b/e2etests/testdata/txtar/sequence-diagram-note-md/dagre/sketch.exp.svg @@ -0,0 +1,866 @@ +xy helloA man who fishes for marlin in ponds + +...dramatic pause + +will put his money in Etruscan bonds. +1 + 1 = 21 + 1 = 2 + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/sequence-diagram-note-md/elk/board.exp.json b/e2etests/testdata/txtar/sequence-diagram-note-md/elk/board.exp.json new file mode 100644 index 000000000..1587ed836 --- /dev/null +++ b/e2etests/testdata/txtar/sequence-diagram-note-md/elk/board.exp.json @@ -0,0 +1,368 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "x", + "type": "rectangle", + "pos": { + "x": 12, + "y": 52 + }, + "width": 100, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "x", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 7, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y", + "type": "rectangle", + "pos": { + "x": 242, + "y": 52 + }, + "width": 100, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "y", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "x.x", + "type": "page", + "pos": { + "x": -128, + "y": 258 + }, + "width": 380, + "height": 119, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "## A man who fishes for marlin in ponds\n\n- ...dramatic pause\n\nwill put his money in Etruscan bonds.", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 380, + "labelHeight": 119, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 5, + "level": 2 + }, + { + "id": "y.z", + "type": "page", + "pos": { + "x": 211, + "y": 447 + }, + "width": 162, + "height": 41, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "\\\\lim_{h \\\\rightarrow 0 } \\\\frac{f(x+h)-f(x)}{h}", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "latex", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 162, + "labelHeight": 41, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 5, + "level": 2 + }, + { + "id": "x.z", + "type": "page", + "pos": { + "x": 10, + "y": 558 + }, + "width": 103, + "height": 37, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "N7", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "1 + 1 = 2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "python", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 87, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 5, + "level": 2 + } + ], + "connections": [ + { + "id": "(x -> y)[0]", + "src": "x", + "srcArrow": "none", + "dst": "y", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "hello", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 33, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 62, + "y": 188 + }, + { + "x": 292, + "y": 188 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 4 + }, + { + "id": "(x -- )[0]", + "src": "x", + "srcArrow": "none", + "dst": "x-lifeline-end-1678191278", + "dstArrow": "none", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "B2", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 62, + "y": 118 + }, + { + "x": 62, + "y": 665 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 1 + }, + { + "id": "(y -- )[0]", + "src": "y", + "srcArrow": "none", + "dst": "y-lifeline-end-35261543", + "dstArrow": "none", + "opacity": 1, + "strokeDash": 6, + "strokeWidth": 2, + "stroke": "B2", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 292, + "y": 118 + }, + { + "x": 292, + "y": 665 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 1 + } + ], + "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/txtar/sequence-diagram-note-md/elk/sketch.exp.svg b/e2etests/testdata/txtar/sequence-diagram-note-md/elk/sketch.exp.svg new file mode 100644 index 000000000..d87897842 --- /dev/null +++ b/e2etests/testdata/txtar/sequence-diagram-note-md/elk/sketch.exp.svg @@ -0,0 +1,866 @@ +xy helloA man who fishes for marlin in ponds + +...dramatic pause + +will put his money in Etruscan bonds. +1 + 1 = 21 + 1 = 2 + + + + + + + + \ No newline at end of file diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index c4be32876..92fe71d6c 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -805,3 +805,176 @@ softwareSystem -> externalSystem } # Include all connections/objects connected to an object (** -> externalSystem)[*]: unsuspend + +-- sequence-diagram-note-md -- +shape: sequence_diagram +x -> y: hello +x.x: |md + ## A man who fishes for marlin in ponds + + - ...dramatic pause + + will put his money in Etruscan bonds. +| + +y.z: |latex + \\lim_{h \\rightarrow 0 } \\frac{f(x+h)-f(x)}{h} +| + +x.z: |python + 1 + 1 = 2 +| + +-- md-label -- +rectangle.shape: rectangle +rectangle: |md + # hello + + - world + + blah blah +| + +square.shape: square +square: |md + # hello + + - world + + blah blah +| + +page.shape: page +page: |md + # hello + + - world + + blah blah +| + +parallelogram.shape: parallelogram +parallelogram: |md + # hello + + - world + + blah blah +| + +document.shape: document +document: |md + # hello + + - world + + blah blah +| + +cylinder.shape: cylinder +cylinder: |md + # hello + + - world + + blah blah +| + +queue.shape: queue +queue: |md + # hello + + - world + + blah blah +| + +package.shape: package +package: |md + # hello + + - world + + blah blah +| + +step.shape: step +step: |md + # hello + + - world + + blah blah +| + +callout.shape: callout +callout: |md + # hello + + - world + + blah blah +| + +stored_data.shape: stored_data +stored_data: |md + # hello + + - world + + blah blah +| + +person.shape: person +person: |md + # hello + + - world + + blah blah +| + +diamond.shape: diamond +diamond: |md + # hello + + - world + + blah blah +| + +oval.shape: oval +oval: |md + # hello + + - world + + blah blah +| + +circle.shape: circle +circle: |md + # hello + + - world + + blah blah +| + +hexagon.shape: hexagon +hexagon: |md + # hello + + - world + + blah blah +| + +cloud.shape: cloud +cloud: |md + # hello + + - world + + blah blah +|
will put his money in Etruscan bonds.