Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add a new shape? #1500

Open
emacsen opened this issue Jun 26, 2020 · 14 comments
Open

How to add a new shape? #1500

emacsen opened this issue Jun 26, 2020 · 14 comments
Labels
Contributor needed Status: Approved Is ready to be worked on Type: New Shape Request for new shape

Comments

@emacsen
Copy link

emacsen commented Jun 26, 2020

I would like to add a custom shape to my diagram, specifically a flow chart, but possibly other types?

I have an SVG file for it. Is there a simple way to add it as a shape in my diagrams, either by using it directly or converting it to another representation and then using it from there?

@emacsen emacsen added Contributor needed Type: Other Not an enhancement or a bug labels Jun 26, 2020
@github-actions github-actions bot added the Status: Triage Needs to be verified, categorized, etc label Jun 26, 2020
@jgreywolf jgreywolf added Type: Question and removed Contributor needed Status: Triage Needs to be verified, categorized, etc Type: Other Not an enhancement or a bug labels Feb 2, 2021
@Polirecyliente
Copy link

Lets not allow this issue to die, a new one will be created with the same request in time, like it has already happened with #820 #548 #274

Adding a feature like this would increase the creative freedom of the users. For example, you could create an SVG with Tikz or with Matplotlib, and then use that as a shape of a flowchart in Mermaid. In this way Mermaid would not need to implement the drawing capabilities of Tikz or Matplotlib, because you would draw there, and then use the drawing in Mermaid.

@Polirecyliente
Copy link

One of the main powers of Mermaid, which is one of the main reasons why some of us even use Mermaid in the first place, is that Mermaid does the layout of shapes in space. Using this definition, "Mermaid does the layout of shapes in space", it would be useful to give custom shapes to Mermaid and have Mermaid then lay them out in space.

@BrutalSimplicity
Copy link

This would make Mermaid even more awesome. In general, I appreciate the styling, syntax, and documentation of Mermaid more than PlantUML, but beyond PlantUML's awkward syntax it has wide support for this already. It's not so pretty, but you can easily incorporate "sprites" into your diagrams. It would be really nice if Mermaid also had this feature.

@jvieille
Copy link

jvieille commented Dec 20, 2022

Any update? The basic shapes offered by Mermaid are extremely limited, specifically for flowcharts which can be used for many different applications.
If "custom shapes" features is a painful feature to implement, may be enriching Mermaid shapes library based on users' requests could be an simple solution?

@sdbbs
Copy link

sdbbs commented Feb 6, 2023

I came up with an example, kind of complicated, but it seems to work in the Online Editor/Mermaid Live Editor; since this is the open issue, I'll post it here. References:

Basically, now that I've found a hoster for SVG (svgur.com), I've uploaded an SVG "cloud" vector image, and I'm using it in an <img> html tag inside a node. This is a screenshot of what I'm seeing in the Online Editor:

Screenshot at 2023-02-06 02-13-20

Here it should be rendered via Github's Mermaid diagrams - and unfortunately, the https://svgshare.com/i/q2Q.svg here is not visible, and the same problem remains if that link is replaced with the cloud_scalable.svg version I uploaded here (and the same problem occurs if you click on Actions/(Download PNG) in Mermaid Live Editor):

%%{init: {'theme':'base'}}%%
flowchart LR
   %% DummyNode("") crashes the parser, must have at least one character
   %%DummyNode("A")

   subgraph Border
       direction LR

       M1["Machine 1"]:::wht;
       subgraph CloudContainer[ ]
           direction LR
           %%CloudImgNode("<img class='BgImgClass' src='https://user-images.githubusercontent.com/48317456/216860238-ada65abe-43e7-45ca-8901-60aa95035eb9.svg' /> <div class='TxtClass'>Subnet <br /> 10.1.1.0/24</div>")
           %% text with <br> does not render with position:absolute
           %% use img top and div padding to center text in image
           CloudImgNode("<img style='position:absolute;left:0;top:7px;color:#fff;' src='https://svgshare.com/i/q2Q.svg' /> <div style='position:relative;z-index:100;color:black;padding:18px;'>Subnet <br /> 10.1.1.0/24 </div>");
       end
       M2["Machine 2"]:::wht;

       M1 --> CloudImgNode;
       CloudImgNode --> M2;
   end

%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes 
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 5 5,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;

%%classDef BgImgClass display:block,position:absolute,left:0,top:0,width:100%;
%%classDef TxtClass display:block,position:absolute,z-index:100,left:0,top:0;
%%classDef InvisibleClass display:none;

%% Custom Styles
%% NOTE: this gets applied to <rect class="basic label-container", 
%% not to the sigbling g<g class="label" or the enclosing <g id="flowchart-CloudImgNode-9174"
%% classdef node class; does however apply the class to the enclosing <g id="flowchart-CloudImgNode-9174" element
%%style CloudImgNode fill:#fff,padding:0,margin:0,stroke:none;
%%style CloudImgNode padding:100px,margin:100px;

%%<div class='TxtClass'>Subnet <br /> 10.1.1.0/24</div>
%% Assigning Nodes to Classes
class Border BorderClass;

%% Cannot add multiple classes separated with , (end up as single word)
%% or space (parser crashes)
%% but thankfully, multiple commands work: they do append classes
%%class DummyNode BgImgClass;
%%class DummyNode TxtClass;
%%class DummyNode InvisibleClass;

class CloudContainer ContainerClass;
%%class CloudImgNode BgImgClass;
%%class CloudImgNode TxtClass;

%% gets applied to all childer, too!
class CloudImgNode ImgNodeClass;
Loading

This should be the rendered .png right from the Mermaid Live Editor - also here the svg is missing:

Note that:

  • There is some leftover padding or margins, so the link lines do not exactly touch the borders of the image
  • Unfortunately, I cannot enter background-image:url("https://some.url.here.com/image.svg") as a classDef in Mermaid, otherwise things would have been easier
  • Unfortunately, I cannot seem to apply classes from classDef to HTML elements written inside a node, possibly because class definitions end up prefixed with #graph-div in the finally generated HTML- otherwsie things would have been easier

Another thing that would have made things easier:

  • Is there a line continuation character, to split a long line into several shorter ones? I tried backslash \ but it didn't work
  • Are there variables, in particular string variables? So I could write, say, $myvar = "url(\"https://some.url.here.com/image.svg\")", - and use it in, say, classDef ImgNodeClass background-image:$myvar,stroke:none;?

Here is the code:

%%{init: {'theme':'base'}}%%
flowchart LR
    %% DummyNode("") crashes the parser, must have at least one character
    %%DummyNode("A")

    subgraph Border
        direction LR

        M1["Machine 1"]:::wht;
        subgraph CloudContainer[ ]
            direction LR
            %%CloudImgNode("<img class='BgImgClass' src='https://svgshare.com/i/q24.svg' /> <div class='TxtClass'>Subnet <br /> 10.1.1.0/24</div>")
            %% text with <br> does not render with position:absolute
            %% use img top and div padding to center text in image
            CloudImgNode("<img style='position:absolute;left:0;top:7px;color:#fff;' src='https://svgshare.com/i/q2Q.svg' /> <div style='position:relative;z-index:100;color:black;padding:18px;'>Subnet <br /> 10.1.1.0/24 </div>");
        end
        M2["Machine 2"]:::wht;

        M1 --> CloudImgNode;
        CloudImgNode --> M2;
    end

%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes 
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 5 5,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;

%%classDef BgImgClass display:block,position:absolute,left:0,top:0,width:100%;
%%classDef TxtClass display:block,position:absolute,z-index:100,left:0,top:0;
%%classDef InvisibleClass display:none;

%% Custom Styles
%% NOTE: this gets applied to <rect class="basic label-container", 
%% not to the sigbling g<g class="label" or the enclosing <g id="flowchart-CloudImgNode-9174"
%% classdef node class; does however apply the class to the enclosing <g id="flowchart-CloudImgNode-9174" element
%%style CloudImgNode fill:#fff,padding:0,margin:0,stroke:none;
%%style CloudImgNode padding:100px,margin:100px;

%%<div class='TxtClass'>Subnet <br /> 10.1.1.0/24</div>
%% Assigning Nodes to Classes
class Border BorderClass;

%% Cannot add multiple classes separated with , (end up as single word)
%% or space (parser crashes)
%% but thankfully, multiple commands work: they do append classes
%%class DummyNode BgImgClass;
%%class DummyNode TxtClass;
%%class DummyNode InvisibleClass;

class CloudContainer ContainerClass;
%%class CloudImgNode BgImgClass;
%%class CloudImgNode TxtClass;

%% gets applied to all childer, too!
class CloudImgNode ImgNodeClass;

@sdbbs
Copy link

sdbbs commented Feb 6, 2023

Well, in relation to previous post - tried image base64 inline embedding instead (https://www.base64-image.de/); and with that, now it is possible to download a correct PNG from Mermaid Live editor... However, Github will unfortunately not render even this kind of svg in the mermaid diagram code:

%%{init: {'theme':'base'}}%%
flowchart LR
    %% DummyNode("") crashes the parser, must have at least one character
    %%DummyNode("A")

    subgraph Border
        direction LR

        M1["Machine 1"]:::wht;
        subgraph CloudContainer[ ]
            direction LR
            %%CloudImgNode("<img class='BgImgClass' src='https://svgshare.com/i/q24.svg' /> <div class='TxtClass'>Subnet <br /> 10.1.1.0/24</div>")
            %% text with <br> does not render with position:absolute
            %% use img top and div padding to center text in ima
            CloudImgNode("<img style='position:absolute;left:0;top:7px;color:#fff;' src=' data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICBpZD0ic3ZnMiIKICAgc29kaXBvZGk6ZG9jbmFtZT0iY2xvdWQuc3ZnIgogICB2aWV3Qm94PSIwIDAgNTY1LjQwOTk1IDMxNi44NDM5OSIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIxLjEuMiAoMToxLjErMjAyMjAyMDUwODUyKzBhMDBjZjUzMzkpIgogICB3aWR0aD0iMTAwJSIKICAgaGVpZ2h0PSIxMDAlIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8ZmlsdGVyCiAgICAgICBpZD0iZmlsdGVyMzMwMSIKICAgICAgIGlua3NjYXBlOm1lbnU9IkFCQ3MiCiAgICAgICBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiCiAgICAgICB3aWR0aD0iMS4wODAxNTgxIgogICAgICAgeT0iLTAuMDcyOTc0ODY4IgogICAgICAgeD0iLTAuMDQwMDc5MDY4IgogICAgICAgaW5rc2NhcGU6bWVudS10b29sdGlwPSJCdWxnaW5nLCBtYXR0ZSBqZWxseSBjb3ZlcmluZyIKICAgICAgIGhlaWdodD0iMS4xNDU5NDk3IgogICAgICAgaW5rc2NhcGU6bGFiZWw9Ik1hdHRlIGplbGx5Ij4KICAgICAgPGZlR2F1c3NpYW5CbHVyCiAgICAgICAgIGlkPSJmZUdhdXNzaWFuQmx1cjMzMDMiCiAgICAgICAgIHJlc3VsdD0icmVzdWx0MCIKICAgICAgICAgc3RkRGV2aWF0aW9uPSIyIgogICAgICAgICBpbj0iU291cmNlQWxwaGEiIC8+CiAgICAgIDxmZUNvbXBvc2l0ZQogICAgICAgICBpZD0iZmVDb21wb3NpdGUzMzA1IgogICAgICAgICBrMz0iMTAiCiAgICAgICAgIGsyPSIxIgogICAgICAgICByZXN1bHQ9InJlc3VsdDMiCiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICBvcGVyYXRvcj0iYXJpdGhtZXRpYyIKICAgICAgICAgaW4yPSJyZXN1bHQwIgogICAgICAgICBrMT0iMCIKICAgICAgICAgazQ9IjAiIC8+CiAgICAgIDxmZUNvbXBvc2l0ZQogICAgICAgICBpZD0iZmVDb21wb3NpdGUzMzA3IgogICAgICAgICBpbjI9InJlc3VsdDMiCiAgICAgICAgIHJlc3VsdD0iZmJTb3VyY2VHcmFwaGljIgogICAgICAgICBpbj0icmVzdWx0MyIKICAgICAgICAgb3BlcmF0b3I9ImluIiAvPgogICAgICA8ZmVDb2xvck1hdHJpeAogICAgICAgICBpZD0iZmVDb2xvck1hdHJpeDM3MjIiCiAgICAgICAgIGluPSJmYlNvdXJjZUdyYXBoaWMiCiAgICAgICAgIHZhbHVlcz0iMSAwIDAgMCAwIDAgMSAwIDAgMCAwIDAgMSAwIDAgMCAwIDAgMC44NSAwIgogICAgICAgICByZXN1bHQ9InJlc3VsdDAiIC8+CiAgICAgIDxmZUdhdXNzaWFuQmx1cgogICAgICAgICBpZD0iZmVHYXVzc2lhbkJsdXIzNzI0IgogICAgICAgICBzdGREZXZpYXRpb249IjciCiAgICAgICAgIGluPSJmYlNvdXJjZUdyYXBoaWNBbHBoYSIgLz4KICAgICAgPGZlU3BlY3VsYXJMaWdodGluZwogICAgICAgICBpZD0iZmVTcGVjdWxhckxpZ2h0aW5nMzcyNiIKICAgICAgICAgcmVzdWx0PSJyZXN1bHQ0IgogICAgICAgICBzcGVjdWxhckNvbnN0YW50PSIwLjg5OTk5OTk4IgogICAgICAgICBzcGVjdWxhckV4cG9uZW50PSIyNSIKICAgICAgICAgc3VyZmFjZVNjYWxlPSI1Ij4KICAgICAgICA8ZmVEaXN0YW50TGlnaHQKICAgICAgICAgICBpZD0iZmVEaXN0YW50TGlnaHQzNzI4IgogICAgICAgICAgIGVsZXZhdGlvbj0iNjAiCiAgICAgICAgICAgYXppbXV0aD0iMjI1IiAvPgogICAgICA8L2ZlU3BlY3VsYXJMaWdodGluZz4KICAgICAgPGZlQ29tcG9zaXRlCiAgICAgICAgIGlkPSJmZUNvbXBvc2l0ZTM3MzAiCiAgICAgICAgIG9wZXJhdG9yPSJhdG9wIgogICAgICAgICBpbjI9InJlc3VsdDAiIC8+CiAgICA8L2ZpbHRlcj4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjI4IgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI3MTIiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTp6b29tPSIwLjciCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjAiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6Y3g9IjIyNS43MTQyOSIKICAgICBpbmtzY2FwZTpjeT0iMjYwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM2NiIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpwYWdlY2hlY2tlcmJvYXJkPSIwIgogICAgIGZpdC1tYXJnaW4tdG9wPSI0IgogICAgIGZpdC1tYXJnaW4tbGVmdD0iNCIKICAgICBmaXQtbWFyZ2luLXJpZ2h0PSI0IgogICAgIGZpdC1tYXJnaW4tYm90dG9tPSI0IiAvPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC03MC4xNTI3LC0zMTMuOTM5KSI+CiAgICA8ZwogICAgICAgaWQ9ImcyMzkzIj4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMjAyIgogICAgICAgICBkPSJtIDQwMC4yMiwzMjAuOTQgYyAtNDkuMDE4LDAgLTkwLjYzOSwzMS4xMjYgLTEwNS42Niw3NC40MDYgLTEyLjc4MiwtNS42MzQ3IC0yNy4wMSwtOC42ODc1IC00MS43MTksLTguNjg3NSAtNTIuODYyLDAgLTk1LjY4OCwzOC45OTcgLTk1LjY4OCw4Ny4xMjUgMCwwLjU4NDkyIDAuMDE4NywxLjE2NzggMC4wMzEyLDEuNzUgLTIuMzY4MiwtMC4yMTg2MiAtNC43NTgyLC0wLjMxMjUgLTcuMTg3NSwtMC4zMTI1IC00MC4yMzgsMCAtNzIuODQ0LDMwLjY5MiAtNzIuODQ0LDY4LjU2MiAwLDM3Ljg3MSAzMi42MDYsNjguNTk0IDcyLjg0NCw2OC41OTQgMjAuMTAzLDAgMzguMzE4LC03LjY3MTIgNTEuNSwtMjAuMDYyIDIwLjE2NCwxMy45NTkgNTEuMDQsMjIuOTA2IDg1LjY1NiwyMi45MDYgMzcuMzg0LDAgNzAuMzczLC0xMC40MjcgOTAuMjUsLTI2LjM0NCAxNC4wNTQsMjAuOTM4IDM5LjM5NCwzNC45MDYgNjguMzEyLDM0LjkwNiAzMi40ODQsMCA2MC40MDcsLTE3LjY0IDcyLjkzOCwtNDIuOTY5IDYxLjIxMywtMi42MTA5IDEwOS45MSwtNDYuMzI3IDEwOS45MSwtOTkuODc1IDAsLTU1LjIyOCAtNTEuODEyLC0xMDAgLTExNS43MiwtMTAwIC00LjM0OSwwIC04LjYxOTMsMC4yMjExMyAtMTIuODQ0LDAuNjI1IC0xOC4zNjQsLTM1Ljk1NiAtNTYuMTM5LC02MC42MjUgLTk5Ljc4MSwtNjAuNjI1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjYiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMjk5OSIKICAgICAgICAgc29kaXBvZGk6cng9Ijk1LjcxNDI4NyIKICAgICAgICAgc29kaXBvZGk6cnk9Ijg3LjE0Mjg2IgogICAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxcHgiCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc29kaXBvZGk6b3Blbj0idHJ1ZSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjQ3My43OTIxOCIKICAgICAgICAgc29kaXBvZGk6Y3g9IjI1Mi44NTc0MiIKICAgICAgICAgc29kaXBvZGk6ZW5kPSI1Ljc4ODg3NyIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjIuOTMyMTI1IgogICAgICAgICBzb2RpcG9kaTphcmMtdHlwZT0iYXJjIgogICAgICAgICBkPSJtIDE1OS4yMzUyOCw0OTEuOTEyNTkgYSA5NS43MTQyODcsODcuMTQyODYgMCAwIDEgNjAuNjMyMjMsLTk5LjkyMzQ4IDk1LjcxNDI4Nyw4Ny4xNDI4NiAwIDAgMSAxMTcuMjQ2OTIsNDAuNDYwNSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMDAzIgogICAgICAgICBzb2RpcG9kaTpyeD0iMTEwIgogICAgICAgICBzb2RpcG9kaTpyeT0iNjEuNDI4NTciCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjFweCIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgICBzb2RpcG9kaTpvcGVuPSJ0cnVlIgogICAgICAgICBzb2RpcG9kaTpjeT0iNTUzLjc5MDc3IgogICAgICAgICBzb2RpcG9kaTpjeD0iMjg3LjE0Mjg1IgogICAgICAgICBzb2RpcG9kaTplbmQ9IjMuMDk1MzIyNiIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjAiCiAgICAgICAgIHNvZGlwb2RpOmFyYy10eXBlPSJhcmMiCiAgICAgICAgIGQ9Ik0gMzk3LjE0Mjg1LDU1My43OTA3NyBBIDExMCw2MS40Mjg1NyAwIDAgMSAyODkuNjg3NDgsNjE1LjIwMjkgMTEwLDYxLjQyODU3IDAgMCAxIDE3Ny4yNjA1OCw1NTYuNjMyMDYiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMjk5NyIKICAgICAgICAgc29kaXBvZGk6cng9IjcyLjg1NzE0IgogICAgICAgICBzb2RpcG9kaTpyeT0iNjguNTcxNDI2IgogICAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxcHgiCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc29kaXBvZGk6b3Blbj0idHJ1ZSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjU0My43OTA3NyIKICAgICAgICAgc29kaXBvZGk6Y3g9IjE1MCIKICAgICAgICAgc29kaXBvZGk6ZW5kPSI1LjI3NTcwNTciCiAgICAgICAgIHNvZGlwb2RpOnN0YXJ0PSI2LjA4ODY2MiIKICAgICAgICAgc29kaXBvZGk6YXJjLXR5cGU9ImFyYyIKICAgICAgICAgZD0ibSAyMjEuNDgzMDUsNTMwLjUzNTk5IGEgNzIuODU3MTQsNjguNTcxNDI2IDAgMCAxIC00My4yNjEzNyw3Ni40NzI4NCA3Mi44NTcxNCw2OC41NzE0MjYgMCAwIDEgLTg4LjMxMjA0NCwtMjQuNDQzMDYgNzIuODU3MTQsNjguNTcxNDI2IDAgMCAxIDcuNjExMDcyLC04Ni4zNDAxNSA3Mi44NTcxNCw2OC41NzE0MjYgMCAwIDEgOTEuMzg0NTIyLC0xMC40MTEyMSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMDA1IgogICAgICAgICBzb2RpcG9kaTpyeD0iODAiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSI3Mi44NTcxNCIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MXB4IgogICAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICAgIHNvZGlwb2RpOm9wZW49InRydWUiCiAgICAgICAgIHNvZGlwb2RpOmN5PSI1NTAuOTMzNTkiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI0NDUuNzE0MjkiCiAgICAgICAgIHNvZGlwb2RpOmVuZD0iNC4xNjQ4NjQ3IgogICAgICAgICBzb2RpcG9kaTpzdGFydD0iNS44Mzk2NDQ3IgogICAgICAgICBzb2RpcG9kaTphcmMtdHlwZT0iYXJjIgogICAgICAgICBkPSJNIDUxNy45NzMzMyw1MTkuNjY3NjcgQSA4MCw3Mi44NTcxNCAwIDAgMSA0ODIuNTI4OTcsNjE1LjYxNzkgODAsNzIuODU3MTQgMCAwIDEgMzc2LjAwNjgzLDU4Ni42ODI2OSA4MCw3Mi44NTcxNCAwIDAgMSA0MDQuMDY4MjksNDg4LjcyNzAxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDMwMDciCiAgICAgICAgIHNvZGlwb2RpOnJ4PSIxMTEuNjUzOTIiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSIxMTAiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjFweCIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgICBzb2RpcG9kaTpvcGVuPSJ0cnVlIgogICAgICAgICBzb2RpcG9kaTpjeT0iNDMwLjkzMzYyIgogICAgICAgICBzb2RpcG9kaTpjeD0iNDAwLjIyNjM1IgogICAgICAgICBzb2RpcG9kaTplbmQ9IjYuMTE0MDA2OSIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjIuNzgxNjI2OSIKICAgICAgICAgc29kaXBvZGk6YXJjLXR5cGU9ImFyYyIKICAgICAgICAgZD0ibSAyOTUuNzI4NDUsNDY5LjY4MDI2IGEgMTExLjY1MzkyLDExMCAwIDAgMSAyMi44NjQ5OCwtMTEzLjc5MzQ4IDExMS42NTM5MiwxMTAgMCAwIDEgMTEzLjY0ODgyLC0zMC4zMzQwMSAxMTEuNjUzOTIsMTEwIDAgMCAxIDc4LjA0Mzk5LDg2Ljg1OTg3IiAvPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDMwMDkiCiAgICAgICAgIHNvZGlwb2RpOnJ4PSIxMTUuNzE0MjkiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSIxMDAiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjFweCIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgICBzb2RpcG9kaTpvcGVuPSJ0cnVlIgogICAgICAgICBzb2RpcG9kaTpjeT0iNDgwLjkzMzYyIgogICAgICAgICBzb2RpcG9kaTpjeD0iNTEyLjg1NzEyIgogICAgICAgICBzb2RpcG9kaTplbmQ9IjIuMTU1NzExMiIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjMuODQ1MTc1IgogICAgICAgICBzb2RpcG9kaTphcmMtdHlwZT0iYXJjIgogICAgICAgICBkPSJNIDQyNC42MjE1Niw0MTYuMjM4MjggQSAxMTUuNzE0MjksMTAwIDAgMCAxIDU4NC4xNzEsNDAyLjE4MTkxIDExNS43MTQyOSwxMDAgMCAwIDEgNjA2LjczMjk2LDUzOS40MDA0MiAxMTUuNzE0MjksMTAwIDAgMCAxIDQ0OC45Njc5Nyw1NjQuMzA5NTMiIC8+CiAgICA8L2c+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTIzIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yaz4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL3B1YmxpY2RvbWFpbi96ZXJvLzEuMC8iIC8+CiAgICAgICAgPGRjOnB1Ymxpc2hlcj4KICAgICAgICAgIDxjYzpBZ2VudAogICAgICAgICAgICAgcmRmOmFib3V0PSJodHRwOi8vb3BlbmNsaXBhcnQub3JnLyI+CiAgICAgICAgICAgIDxkYzp0aXRsZT5PcGVuY2xpcGFydDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICA8L2NjOldvcms+CiAgICAgIDxjYzpMaWNlbnNlCiAgICAgICAgIHJkZjphYm91dD0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluLyI+CiAgICAgICAgPGNjOnBlcm1pdHMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zI1JlcHJvZHVjdGlvbiIgLz4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjRGlzdHJpYnV0aW9uIiAvPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyNEZXJpdmF0aXZlV29ya3MiIC8+CiAgICAgIDwvY2M6TGljZW5zZT4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgo8L3N2Zz4K' /> <div style='position:relative;z-index:100;color:black;padding:18px;'>Subnet <br /> 10.1.1.0/24 </div>");
        end
        M2["Machine 2"]:::wht;

        M1 --> CloudImgNode;
        CloudImgNode --> M2;
    end

%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes 
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 5 5,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;

%%classDef BgImgClass display:block,position:absolute,left:0,top:0,width:100%;
%%classDef TxtClass display:block,position:absolute,z-index:100,left:0,top:0;
%%classDef InvisibleClass display:none;

%% Custom Styles
%% NOTE: this gets applied to <rect class="basic label-container", 
%% not to the sigbling g<g class="label" or the enclosing <g id="flowchart-CloudImgNode-9174"
%% classdef node class; does however apply the class to the enclosing <g id="flowchart-CloudImgNode-9174" element
%%style CloudImgNode fill:#fff,padding:0,margin:0,stroke:none;
%%style CloudImgNode padding:100px,margin:100px;

%%<div class='TxtClass'>Subnet <br /> 10.1.1.0/24</div>
%% Assigning Nodes to Classes
class Border BorderClass;

%% Cannot add multiple classes separated with , (end up as single word)
%% or space (parser crashes)
%% but thankfully, multiple commands work: they do append classes
%%class DummyNode BgImgClass;
%%class DummyNode TxtClass;
%%class DummyNode InvisibleClass;

class CloudContainer ContainerClass;
%%class CloudImgNode BgImgClass;
%%class CloudImgNode TxtClass;

%% gets applied to all childer, too!
class CloudImgNode ImgNodeClass;
Loading

However, the PNG export from the Mermaid Live editor works - unfortunately, Github refuses to render the Markdown link from Mermaid Live editor works (probably because it is too long?):

... and Github even refuses to parse the standalone PNG link as an URL.

In any case, I'll just post the changed line, just to point out how less readable everything becomes with each approach:

CloudImgNode("<img style='position:absolute;left:0;top:7px;color:#fff;' src=' data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICBpZD0ic3ZnMiIKICAgc29kaXBvZGk6ZG9jbmFtZT0iY2xvdWQuc3ZnIgogICB2aWV3Qm94PSIwIDAgNTY1LjQwOTk1IDMxNi44NDM5OSIKICAgdmVyc2lvbj0iMS4xIgogICBpbmtzY2FwZTp2ZXJzaW9uPSIxLjEuMiAoMToxLjErMjAyMjAyMDUwODUyKzBhMDBjZjUzMzkpIgogICB3aWR0aD0iMTAwJSIKICAgaGVpZ2h0PSIxMDAlIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCI+CiAgICA8ZmlsdGVyCiAgICAgICBpZD0iZmlsdGVyMzMwMSIKICAgICAgIGlua3NjYXBlOm1lbnU9IkFCQ3MiCiAgICAgICBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiCiAgICAgICB3aWR0aD0iMS4wODAxNTgxIgogICAgICAgeT0iLTAuMDcyOTc0ODY4IgogICAgICAgeD0iLTAuMDQwMDc5MDY4IgogICAgICAgaW5rc2NhcGU6bWVudS10b29sdGlwPSJCdWxnaW5nLCBtYXR0ZSBqZWxseSBjb3ZlcmluZyIKICAgICAgIGhlaWdodD0iMS4xNDU5NDk3IgogICAgICAgaW5rc2NhcGU6bGFiZWw9Ik1hdHRlIGplbGx5Ij4KICAgICAgPGZlR2F1c3NpYW5CbHVyCiAgICAgICAgIGlkPSJmZUdhdXNzaWFuQmx1cjMzMDMiCiAgICAgICAgIHJlc3VsdD0icmVzdWx0MCIKICAgICAgICAgc3RkRGV2aWF0aW9uPSIyIgogICAgICAgICBpbj0iU291cmNlQWxwaGEiIC8+CiAgICAgIDxmZUNvbXBvc2l0ZQogICAgICAgICBpZD0iZmVDb21wb3NpdGUzMzA1IgogICAgICAgICBrMz0iMTAiCiAgICAgICAgIGsyPSIxIgogICAgICAgICByZXN1bHQ9InJlc3VsdDMiCiAgICAgICAgIGluPSJTb3VyY2VHcmFwaGljIgogICAgICAgICBvcGVyYXRvcj0iYXJpdGhtZXRpYyIKICAgICAgICAgaW4yPSJyZXN1bHQwIgogICAgICAgICBrMT0iMCIKICAgICAgICAgazQ9IjAiIC8+CiAgICAgIDxmZUNvbXBvc2l0ZQogICAgICAgICBpZD0iZmVDb21wb3NpdGUzMzA3IgogICAgICAgICBpbjI9InJlc3VsdDMiCiAgICAgICAgIHJlc3VsdD0iZmJTb3VyY2VHcmFwaGljIgogICAgICAgICBpbj0icmVzdWx0MyIKICAgICAgICAgb3BlcmF0b3I9ImluIiAvPgogICAgICA8ZmVDb2xvck1hdHJpeAogICAgICAgICBpZD0iZmVDb2xvck1hdHJpeDM3MjIiCiAgICAgICAgIGluPSJmYlNvdXJjZUdyYXBoaWMiCiAgICAgICAgIHZhbHVlcz0iMSAwIDAgMCAwIDAgMSAwIDAgMCAwIDAgMSAwIDAgMCAwIDAgMC44NSAwIgogICAgICAgICByZXN1bHQ9InJlc3VsdDAiIC8+CiAgICAgIDxmZUdhdXNzaWFuQmx1cgogICAgICAgICBpZD0iZmVHYXVzc2lhbkJsdXIzNzI0IgogICAgICAgICBzdGREZXZpYXRpb249IjciCiAgICAgICAgIGluPSJmYlNvdXJjZUdyYXBoaWNBbHBoYSIgLz4KICAgICAgPGZlU3BlY3VsYXJMaWdodGluZwogICAgICAgICBpZD0iZmVTcGVjdWxhckxpZ2h0aW5nMzcyNiIKICAgICAgICAgcmVzdWx0PSJyZXN1bHQ0IgogICAgICAgICBzcGVjdWxhckNvbnN0YW50PSIwLjg5OTk5OTk4IgogICAgICAgICBzcGVjdWxhckV4cG9uZW50PSIyNSIKICAgICAgICAgc3VyZmFjZVNjYWxlPSI1Ij4KICAgICAgICA8ZmVEaXN0YW50TGlnaHQKICAgICAgICAgICBpZD0iZmVEaXN0YW50TGlnaHQzNzI4IgogICAgICAgICAgIGVsZXZhdGlvbj0iNjAiCiAgICAgICAgICAgYXppbXV0aD0iMjI1IiAvPgogICAgICA8L2ZlU3BlY3VsYXJMaWdodGluZz4KICAgICAgPGZlQ29tcG9zaXRlCiAgICAgICAgIGlkPSJmZUNvbXBvc2l0ZTM3MzAiCiAgICAgICAgIG9wZXJhdG9yPSJhdG9wIgogICAgICAgICBpbjI9InJlc3VsdDAiIC8+CiAgICA8L2ZpbHRlcj4KICA8L2RlZnM+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIGlkPSJiYXNlIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjI4IgogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSI3MTIiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTp6b29tPSIwLjciCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjAiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgaW5rc2NhcGU6Y3g9IjIyNS43MTQyOSIKICAgICBpbmtzY2FwZTpjeT0iMjYwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTM2NiIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMC4wIgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpwYWdlY2hlY2tlcmJvYXJkPSIwIgogICAgIGZpdC1tYXJnaW4tdG9wPSI0IgogICAgIGZpdC1tYXJnaW4tbGVmdD0iNCIKICAgICBmaXQtbWFyZ2luLXJpZ2h0PSI0IgogICAgIGZpdC1tYXJnaW4tYm90dG9tPSI0IiAvPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICBpbmtzY2FwZTpsYWJlbD0iTGF5ZXIgMSIKICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIgogICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC03MC4xNTI3LC0zMTMuOTM5KSI+CiAgICA8ZwogICAgICAgaWQ9ImcyMzkzIj4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMjAyIgogICAgICAgICBkPSJtIDQwMC4yMiwzMjAuOTQgYyAtNDkuMDE4LDAgLTkwLjYzOSwzMS4xMjYgLTEwNS42Niw3NC40MDYgLTEyLjc4MiwtNS42MzQ3IC0yNy4wMSwtOC42ODc1IC00MS43MTksLTguNjg3NSAtNTIuODYyLDAgLTk1LjY4OCwzOC45OTcgLTk1LjY4OCw4Ny4xMjUgMCwwLjU4NDkyIDAuMDE4NywxLjE2NzggMC4wMzEyLDEuNzUgLTIuMzY4MiwtMC4yMTg2MiAtNC43NTgyLC0wLjMxMjUgLTcuMTg3NSwtMC4zMTI1IC00MC4yMzgsMCAtNzIuODQ0LDMwLjY5MiAtNzIuODQ0LDY4LjU2MiAwLDM3Ljg3MSAzMi42MDYsNjguNTk0IDcyLjg0NCw2OC41OTQgMjAuMTAzLDAgMzguMzE4LC03LjY3MTIgNTEuNSwtMjAuMDYyIDIwLjE2NCwxMy45NTkgNTEuMDQsMjIuOTA2IDg1LjY1NiwyMi45MDYgMzcuMzg0LDAgNzAuMzczLC0xMC40MjcgOTAuMjUsLTI2LjM0NCAxNC4wNTQsMjAuOTM4IDM5LjM5NCwzNC45MDYgNjguMzEyLDM0LjkwNiAzMi40ODQsMCA2MC40MDcsLTE3LjY0IDcyLjkzOCwtNDIuOTY5IDYxLjIxMywtMi42MTA5IDEwOS45MSwtNDYuMzI3IDEwOS45MSwtOTkuODc1IDAsLTU1LjIyOCAtNTEuODEyLC0xMDAgLTExNS43MiwtMTAwIC00LjM0OSwwIC04LjYxOTMsMC4yMjExMyAtMTIuODQ0LDAuNjI1IC0xOC4zNjQsLTM1Ljk1NiAtNTYuMTM5LC02MC42MjUgLTk5Ljc4MSwtNjAuNjI1IHoiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjYiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMjk5OSIKICAgICAgICAgc29kaXBvZGk6cng9Ijk1LjcxNDI4NyIKICAgICAgICAgc29kaXBvZGk6cnk9Ijg3LjE0Mjg2IgogICAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxcHgiCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc29kaXBvZGk6b3Blbj0idHJ1ZSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjQ3My43OTIxOCIKICAgICAgICAgc29kaXBvZGk6Y3g9IjI1Mi44NTc0MiIKICAgICAgICAgc29kaXBvZGk6ZW5kPSI1Ljc4ODg3NyIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjIuOTMyMTI1IgogICAgICAgICBzb2RpcG9kaTphcmMtdHlwZT0iYXJjIgogICAgICAgICBkPSJtIDE1OS4yMzUyOCw0OTEuOTEyNTkgYSA5NS43MTQyODcsODcuMTQyODYgMCAwIDEgNjAuNjMyMjMsLTk5LjkyMzQ4IDk1LjcxNDI4Nyw4Ny4xNDI4NiAwIDAgMSAxMTcuMjQ2OTIsNDAuNDYwNSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMDAzIgogICAgICAgICBzb2RpcG9kaTpyeD0iMTEwIgogICAgICAgICBzb2RpcG9kaTpyeT0iNjEuNDI4NTciCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjFweCIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgICBzb2RpcG9kaTpvcGVuPSJ0cnVlIgogICAgICAgICBzb2RpcG9kaTpjeT0iNTUzLjc5MDc3IgogICAgICAgICBzb2RpcG9kaTpjeD0iMjg3LjE0Mjg1IgogICAgICAgICBzb2RpcG9kaTplbmQ9IjMuMDk1MzIyNiIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjAiCiAgICAgICAgIHNvZGlwb2RpOmFyYy10eXBlPSJhcmMiCiAgICAgICAgIGQ9Ik0gMzk3LjE0Mjg1LDU1My43OTA3NyBBIDExMCw2MS40Mjg1NyAwIDAgMSAyODkuNjg3NDgsNjE1LjIwMjkgMTEwLDYxLjQyODU3IDAgMCAxIDE3Ny4yNjA1OCw1NTYuNjMyMDYiIC8+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMjk5NyIKICAgICAgICAgc29kaXBvZGk6cng9IjcyLjg1NzE0IgogICAgICAgICBzb2RpcG9kaTpyeT0iNjguNTcxNDI2IgogICAgICAgICBzdHlsZT0iZmlsbDojZmZmZmZmO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxcHgiCiAgICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyIKICAgICAgICAgc29kaXBvZGk6b3Blbj0idHJ1ZSIKICAgICAgICAgc29kaXBvZGk6Y3k9IjU0My43OTA3NyIKICAgICAgICAgc29kaXBvZGk6Y3g9IjE1MCIKICAgICAgICAgc29kaXBvZGk6ZW5kPSI1LjI3NTcwNTciCiAgICAgICAgIHNvZGlwb2RpOnN0YXJ0PSI2LjA4ODY2MiIKICAgICAgICAgc29kaXBvZGk6YXJjLXR5cGU9ImFyYyIKICAgICAgICAgZD0ibSAyMjEuNDgzMDUsNTMwLjUzNTk5IGEgNzIuODU3MTQsNjguNTcxNDI2IDAgMCAxIC00My4yNjEzNyw3Ni40NzI4NCA3Mi44NTcxNCw2OC41NzE0MjYgMCAwIDEgLTg4LjMxMjA0NCwtMjQuNDQzMDYgNzIuODU3MTQsNjguNTcxNDI2IDAgMCAxIDcuNjExMDcyLC04Ni4zNDAxNSA3Mi44NTcxNCw2OC41NzE0MjYgMCAwIDEgOTEuMzg0NTIyLC0xMC40MTEyMSIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgzMDA1IgogICAgICAgICBzb2RpcG9kaTpyeD0iODAiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSI3Mi44NTcxNCIKICAgICAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MXB4IgogICAgICAgICBzb2RpcG9kaTp0eXBlPSJhcmMiCiAgICAgICAgIHNvZGlwb2RpOm9wZW49InRydWUiCiAgICAgICAgIHNvZGlwb2RpOmN5PSI1NTAuOTMzNTkiCiAgICAgICAgIHNvZGlwb2RpOmN4PSI0NDUuNzE0MjkiCiAgICAgICAgIHNvZGlwb2RpOmVuZD0iNC4xNjQ4NjQ3IgogICAgICAgICBzb2RpcG9kaTpzdGFydD0iNS44Mzk2NDQ3IgogICAgICAgICBzb2RpcG9kaTphcmMtdHlwZT0iYXJjIgogICAgICAgICBkPSJNIDUxNy45NzMzMyw1MTkuNjY3NjcgQSA4MCw3Mi44NTcxNCAwIDAgMSA0ODIuNTI4OTcsNjE1LjYxNzkgODAsNzIuODU3MTQgMCAwIDEgMzc2LjAwNjgzLDU4Ni42ODI2OSA4MCw3Mi44NTcxNCAwIDAgMSA0MDQuMDY4MjksNDg4LjcyNzAxIiAvPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDMwMDciCiAgICAgICAgIHNvZGlwb2RpOnJ4PSIxMTEuNjUzOTIiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSIxMTAiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjFweCIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgICBzb2RpcG9kaTpvcGVuPSJ0cnVlIgogICAgICAgICBzb2RpcG9kaTpjeT0iNDMwLjkzMzYyIgogICAgICAgICBzb2RpcG9kaTpjeD0iNDAwLjIyNjM1IgogICAgICAgICBzb2RpcG9kaTplbmQ9IjYuMTE0MDA2OSIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjIuNzgxNjI2OSIKICAgICAgICAgc29kaXBvZGk6YXJjLXR5cGU9ImFyYyIKICAgICAgICAgZD0ibSAyOTUuNzI4NDUsNDY5LjY4MDI2IGEgMTExLjY1MzkyLDExMCAwIDAgMSAyMi44NjQ5OCwtMTEzLjc5MzQ4IDExMS42NTM5MiwxMTAgMCAwIDEgMTEzLjY0ODgyLC0zMC4zMzQwMSAxMTEuNjUzOTIsMTEwIDAgMCAxIDc4LjA0Mzk5LDg2Ljg1OTg3IiAvPgogICAgICA8cGF0aAogICAgICAgICBpZD0icGF0aDMwMDkiCiAgICAgICAgIHNvZGlwb2RpOnJ4PSIxMTUuNzE0MjkiCiAgICAgICAgIHNvZGlwb2RpOnJ5PSIxMDAiCiAgICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjFweCIKICAgICAgICAgc29kaXBvZGk6dHlwZT0iYXJjIgogICAgICAgICBzb2RpcG9kaTpvcGVuPSJ0cnVlIgogICAgICAgICBzb2RpcG9kaTpjeT0iNDgwLjkzMzYyIgogICAgICAgICBzb2RpcG9kaTpjeD0iNTEyLjg1NzEyIgogICAgICAgICBzb2RpcG9kaTplbmQ9IjIuMTU1NzExMiIKICAgICAgICAgc29kaXBvZGk6c3RhcnQ9IjMuODQ1MTc1IgogICAgICAgICBzb2RpcG9kaTphcmMtdHlwZT0iYXJjIgogICAgICAgICBkPSJNIDQyNC42MjE1Niw0MTYuMjM4MjggQSAxMTUuNzE0MjksMTAwIDAgMCAxIDU4NC4xNzEsNDAyLjE4MTkxIDExNS43MTQyOSwxMDAgMCAwIDEgNjA2LjczMjk2LDUzOS40MDA0MiAxMTUuNzE0MjksMTAwIDAgMCAxIDQ0OC45Njc5Nyw1NjQuMzA5NTMiIC8+CiAgICA8L2c+CiAgPC9nPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTIzIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yaz4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8+CiAgICAgICAgPGNjOmxpY2Vuc2UKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL3B1YmxpY2RvbWFpbi96ZXJvLzEuMC8iIC8+CiAgICAgICAgPGRjOnB1Ymxpc2hlcj4KICAgICAgICAgIDxjYzpBZ2VudAogICAgICAgICAgICAgcmRmOmFib3V0PSJodHRwOi8vb3BlbmNsaXBhcnQub3JnLyI+CiAgICAgICAgICAgIDxkYzp0aXRsZT5PcGVuY2xpcGFydDwvZGM6dGl0bGU+CiAgICAgICAgICA8L2NjOkFnZW50PgogICAgICAgIDwvZGM6cHVibGlzaGVyPgogICAgICA8L2NjOldvcms+CiAgICAgIDxjYzpMaWNlbnNlCiAgICAgICAgIHJkZjphYm91dD0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvcHVibGljZG9tYWluLyI+CiAgICAgICAgPGNjOnBlcm1pdHMKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zI1JlcHJvZHVjdGlvbiIgLz4KICAgICAgICA8Y2M6cGVybWl0cwogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjRGlzdHJpYnV0aW9uIiAvPgogICAgICAgIDxjYzpwZXJtaXRzCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyNEZXJpdmF0aXZlV29ya3MiIC8+CiAgICAgIDwvY2M6TGljZW5zZT4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgo8L3N2Zz4K' /> <div style='position:relative;z-index:100;color:black;padding:18px;'>Subnet <br /> 10.1.1.0/24 </div>");

@sdbbs
Copy link

sdbbs commented Feb 6, 2023

Another rework of the previous example, this time using FontAwesome - not even this renders in Github mermaid diagram:

%%{init: {'theme':'base'}}%%
flowchart LR

    subgraph Border
        direction LR

        M1["Machine 1"]:::wht;
        subgraph CloudContainer[ ]
            direction LR
            %% text with <br> does not render with position:absolute
            %% use img top and div padding to center text in image; 
            %% color:#c8c8c8 seems not to work here - must escape the # as #35;
            CloudImgNode("<div style='position:absolute;left:8px;top:-20px;color:#35;bbb;font-size:80px;'>fa:fa-cloud</div> <div style='position:relative;z-index:100;color:black;padding:18px;'>Subnet <br /> 10.1.1.0/24 </div>");
        end
        M2["Machine 2"]:::wht;

        M1 --> CloudImgNode;
        CloudImgNode --> M2;
    end

%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes 
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 5 5,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;

%% Custom Styles

%% Assigning Nodes to Classes
%% class gets applied to all childer, too!
class Border BorderClass;
class CloudContainer ContainerClass;
class CloudImgNode ImgNodeClass;
Loading

At least PNG direct markdown link from Mermaid Live editor works - unfortunately, the alignment of the icon you see in the Live editor and the png below will not be the same (the below is tuned for PNG output)

... and at last I tried with the Unicode symbol for cloud - and this seems to work also in Github mermaid diagram:

%%{init: {'theme':'base'}}%%
flowchart LR

    subgraph Border
        direction LR

        M1["Machine 1"]:::wht;
        subgraph CloudContainer[ ]
            direction LR
            %% text with <br> does not render with position:absolute
            %% use img top and div padding to center text in image; color:#c8c8c8 seems not to work?
            CloudImgNode("<div style='position:absolute;left:12px;top:-10px;color:#35;bbb;font-size:80px;'>#9729;#65039;</div> <div style='position:relative;z-index:100;color:black;padding:18px;'>Subnet <br /> 10.1.1.0/24 </div>");
        end
        M2["Machine 2"]:::wht;

        M1 --> CloudImgNode;
        CloudImgNode --> M2;
    end

%% Defining Class Styles
%% background-image:"" passes, but not with any text inside quotes
%% node that classes here get defined with "#graph-div " prepended!
%% however, seemingly <img> and such are included via foreignObject, so cannot refer to these classes 
classDef BorderClass fill:#fff,stroke:#fff,stroke-width:4px,color:#fff,stroke-dasharray: 5 5,margin:0,padding:0;
classDef ContainerClass position:relative,padding:0,margin:0,fill:#fff,color:#000,stroke:none;
classDef ImgNodeClass fill:#fff,padding:0,margin:0,stroke:none,margin:0;
classDef wht fill:#fff,color:black,stroke:#000;

%% Custom Styles

%% Assigning Nodes to Classes
%% class gets applied to all childer, too!
class Border BorderClass;
class CloudContainer ContainerClass;
class CloudImgNode ImgNodeClass;
Loading

@jgreywolf
Copy link
Contributor

@sdbbs Would you like to officially take this issue on? :)

@jgreywolf
Copy link
Contributor

More information available in #1250

@jgreywolf jgreywolf added Type: New Shape Request for new shape and removed Type: Enhancement New feature or request labels Mar 8, 2023
@nair-sumesh
Copy link

Shape for actors

@rickdgray-dc
Copy link

I think svg support is definitely the route to go. Simple ones are extremely compact compared to rendered images so they can easily be embedded into the markdown. I am wanting a simple cloud shape as well but for now will have to just use a box.

@samjco
Copy link

samjco commented Apr 29, 2024

So it seems that the shapes are first created as points and added as functions.

For Example, (flowchart shapes) notice the function found at:
https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/diagrams/flowchart/flowChartShapes.js:

To Make a Trapezoid, notice the function on Line 120:

function trapezoid(parent, bbox, node) {
  const w = bbox.width;
  const h = bbox.height;
  const points = [
    { x: (-2 * h) / 6, y: 0 },
    { x: w + (2 * h) / 6, y: 0 },
    { x: w - h / 6, y: -h },
    { x: h / 6, y: -h },
  ];
  const shapeSvg = insertPolygonShape(parent, w, h, points);
  node.intersect = function (point) {
    return intersectPolygon(node, points, point);
  };
  return shapeSvg;
}

So make inorder to make new shape, there would need to be a need a way to convert a SVG code into js points.

Example SVG:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
    <polygon points="10,10 40,10 30,40" fill="none" stroke="black" />
    <polyline points="50,10 70,10 70,30 50,30" fill="none" stroke="blue" />
</svg>

Python script (untested):

import xml.etree.ElementTree as ET

def parse_svg(svg_path):
    """Parse the SVG file and convert its shapes into JavaScript code."""
    # Parse the SVG file
    tree = ET.parse(svg_path)
    root = tree.getroot()

    for element in root.iter():
        if element.tag.endswith('polygon'):
            # Parse the points attribute into a list of coordinate tuples
            points = [(float(x), float(y)) for x, y in (p.split(',') for p in element.get('points').split())]

            # Get the bounding box width and height
            w = max(p[0] for p in points) - min(p[0] for p in points)
            h = max(p[1] for p in points) - min(p[1] for p in points)

            # Convert points into JavaScript object literals
            js_points = [
                f"{{ x: {p[0]}, y: {p[1]} }}"
                for p in points
            ]

            # Create a JavaScript code snippet
            js_code = f"const points = [{', '.join(js_points)}];"
            print(f"JavaScript Code:\n{js_code}\n")

def parse_svg_file(svg_path):
    """Parse an SVG file and print JavaScript code for each shape."""
    parse_svg(svg_path)

# Usage
parse_svg_file('example.svg')

Example output:

const points = [{ x: 10, y: 10 }, { x: 40, y: 10 }, { x: 30, y: 40 }];

So out NEW function may look like this:

function myNewShape(parent, bbox, node) {
  const w = bbox.width;
  const h = bbox.height;
  const points = [
{ x: 10, y: 10 }, 
{ x: 40, y: 10 }, 
{ x: 30, y: 40 }
  ];
  const shapeSvg = insertPolygonShape(parent, w, h, points);
  node.intersect = function (point) {
    return intersectPolygon(node, points, point);
  };
  return shapeSvg;
}

Please let me know if this works??

@samjco
Copy link

samjco commented Apr 29, 2024

example.svg:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
    <polygon points="10,10 40,10 30,40" fill="none" stroke="black" />
    <polyline points="50,10 70,10 70,30 50,30" fill="none" stroke="blue" />
</svg>

Other script versions:

Using PHP

<?php
function parse_svg($svg_path) {
    $xml = simplexml_load_file($svg_path);

    foreach ($xml->children() as $element) {
        $tag = strtolower($element->getName());

        if ($tag === "polygon") {
            $points = (string) $element['points'];
            $point_pairs = explode(" ", $points);

            $js_points = array_map(function ($pair) {
                list($x, $y) = explode(",", $pair);
                return "{ x: " . floatval($x) . ", y: " . floatval($y) . " }";
            }, $point_pairs);

            $js_code_content = implode(", ", $js_points);

            echo "JavaScript Array Content:\n" . $js_code_content . "\n\n";
        }
        // Additional logic for other shape types can be added here
    }
}

parse_svg('example.svg');
?>

Script Functionality:

SimpleXML: The script uses PHP's simplexml_load_file function to parse the SVG file.
Polygon Points: Points are parsed from the points attribute, split into pairs, and converted into JavaScript object literals.
JavaScript Snippet: These literals are concatenated into a string representing the contents of the points array.
Output: The script prints this string to the console.

OR Using JS

const fs = require('fs');
const { DOMParser } = require('xmldom');

function parseSVG(svgPath) {
    // Read the SVG file as a string
    const svgContent = fs.readFileSync(svgPath, 'utf8');

    // Parse the SVG content into a DOM tree
    const doc = new DOMParser().parseFromString(svgContent, 'text/xml');

    const polygons = doc.getElementsByTagName('polygon');

    for (let i = 0; i < polygons.length; i++) {
        const points = polygons[i].getAttribute('points');
        const pointPairs = points.split(' ');

        const jsPoints = pointPairs.map(pair => {
            const [x, y] = pair.split(',');
            return `{ x: ${parseFloat(x)}, y: ${parseFloat(y)} }`;
        });

        const jsCodeContent = jsPoints.join(', ');

        console.log(`JavaScript Array Content:\n${jsCodeContent}\n`);
    }
}

// Usage
parseSVG('example.svg');

Script Functionality:

FS Module: The script uses Node.js's fs module to read the SVG file as a string.
DOMParser: It uses the DOMParser class to convert the SVG content into a DOM tree.
Polygon Points: Points are extracted from the points attribute, split into pairs, and converted into JavaScript object literals.
JavaScript Snippet: The literals are concatenated into a string representing the array contents.
Output: The script prints this string to the console.

Output:
{ x: 10, y: 10 }, { x: 40, y: 10 }, { x: 30, y: 40 }

Conclusion:
Both scripts (PHP and JavaScript) parse the SVG file, extract its coordinates, and output them in a format suitable for JavaScript arrays. You can adapt and extend these scripts to handle various shapes or integrate them directly into your workflows.

@samjco
Copy link

samjco commented May 2, 2024

Ah Fontawesome support is added.
Now, how can we make custom FA icons
https://fontawesome.com/search?o=r&f=brands

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Contributor needed Status: Approved Is ready to be worked on Type: New Shape Request for new shape
Projects
None yet
Development

No branches or pull requests

9 participants