mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
72.34k stars 6.59k forks source link

Incorrect bounding box for htmlLabels with images in Mermaid v10.2.0 #4455

Closed aloisklink closed 1 year ago

aloisklink commented 1 year ago

Description

Images in flowcharts used to work fine in Mermaid v10.1.0 or before, but in Mermaid v10.2.0, the boxes are now smaller, and longer automatically expand to fit the entire image.

This was caught by mermaid-cli's automated visual regression tests, see https://github.com/mermaid-js/mermaid-cli/pull/541#pullrequestreview-1449884035

Steps to reproduce

Copy in the code sample below in Mermaid v10.1.0 and v10.2.0 (or commit d132d26246dc480b57e74c7ed94dc984207c4895 and 9bb0cef82bba4f31878852c9bf8075964652b93f).

However, they both do not work with the mermaid.live editor. So you may find it easier to add the following patch to the demos/flowchart.html file:

diff --git a/demos/flowchart.html b/demos/flowchart.html
index 02405c5e..f259717a 100644
--- a/demos/flowchart.html
+++ b/demos/flowchart.html
@@ -1505,6 +1505,16 @@
     </pre>
     <hr />

+    <pre class="mermaid">
+      graph TD
+      B[&quot;fa:fa-car for peace&quot;]
+      B--&gt;C[fa:fa-ban forbidden]
+      B--&gt;D(fa:fa-spinner);
+      B--&gt;E(A fa:fa-camera-retro perhaps?);
+      %% Test whether embed &lt;img&gt; work correctly
+      D--&gt;F(&quot;&lt;img height='100' width='100' src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22%3E%3Ccircle cx=%2250%22 cy=%2250%22 r=%2240%22 stroke=%22black%22 stroke-width=%223%22 fill=%22red%22 /%3E%3C/svg%3E'/&gt; &lt;br/&gt; Red Circle&quot;)
+    </pre>
+
     <h1 id="link-clicked">Anchor for "link-clicked" test</h1>

     <script type="module">

Screenshots

Working in Mermaid v10.1.0

SVG (may miss fontawesome fonts when rendered on GitHub)

v10-1-0

PNG

v10-1-0

Broken in Mermaid v10.2.0

SVG (may miss fontawesome fonts when rendered on GitHub)

v10-2-0

PNG

v10-2-0

Code Sample

graph TD
    B["fa:fa-car for peace"]
    B-->C[fa:fa-ban forbidden]
    B-->D(fa:fa-spinner);
    B-->E(A fa:fa-camera-retro perhaps?);
    %% Test whether embed <img> work correctly
    D-->F("<img height='100' width='100' src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22%3E%3Ccircle cx=%2250%22 cy=%2250%22 r=%2240%22 stroke=%22black%22 stroke-width=%223%22 fill=%22red%22 /%3E%3C/svg%3E'/> <br/> Red Circle")

Setup

Additional Context

I've done a diff on the output SVGs, after passing the SVGs through an XML prettier to make it a bit prettier:

< <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -8 353.140625 322" height="322"
---
> <svg aria-roledescription="flowchart-v2" role="graphics-document document" viewBox="-8 -8 353.140625 337" height="337"
4c4
<     <style>#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:2px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#000000;stroke:#000000;}#my-svg .marker.cross{stroke:#000000;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span,#my-svg p{color:#333;}#my-svg .label text,#my-svg span,#my-svg p{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#cde498;stroke:#13540c;stroke-width:1px;}#my-svg .flowchart-label text{text-anchor:middle;}#my-svg .node .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .arrowheadPath{fill:green;}#my-svg .edgePath .path{stroke:#000000;stroke-width:2.0px;}#my-svg .flowchart-link{stroke:#000000;fill:none;}#my-svg .edgeLabel{background-color:#e8e8e8;text-align:center;}#my-svg .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#my-svg .cluster rect{fill:#cdffb2;stroke:#6eaa49;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span,#my-svg p{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(78.1578947368, 58.4615384615%, 84.5098039216%);border:1px solid #6eaa49;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg .node rect{fill:white;}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style>
---
>     <style>#my-svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#000000;}#my-svg .error-icon{fill:#552222;}#my-svg .error-text{fill:#552222;stroke:#552222;}#my-svg .edge-thickness-normal{stroke-width:2px;}#my-svg .edge-thickness-thick{stroke-width:3.5px;}#my-svg .edge-pattern-solid{stroke-dasharray:0;}#my-svg .edge-pattern-dashed{stroke-dasharray:3;}#my-svg .edge-pattern-dotted{stroke-dasharray:2;}#my-svg .marker{fill:#000000;stroke:#000000;}#my-svg .marker.cross{stroke:#000000;}#my-svg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#my-svg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#000000;}#my-svg .cluster-label text{fill:#333;}#my-svg .cluster-label span,#my-svg p{color:#333;}#my-svg .label text,#my-svg span,#my-svg p{fill:#000000;color:#000000;}#my-svg .node rect,#my-svg .node circle,#my-svg .node ellipse,#my-svg .node polygon,#my-svg .node path{fill:#cde498;stroke:#13540c;stroke-width:1px;}#my-svg .flowchart-label text{text-anchor:middle;}#my-svg .node .label{text-align:center;}#my-svg .node.clickable{cursor:pointer;}#my-svg .arrowheadPath{fill:green;}#my-svg .edgePath .path{stroke:#000000;stroke-width:2.0px;}#my-svg .flowchart-link{stroke:#000000;fill:none;}#my-svg .edgeLabel{background-color:#e8e8e8;text-align:center;}#my-svg .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#my-svg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#my-svg .cluster rect{fill:#cdffb2;stroke:#6eaa49;stroke-width:1px;}#my-svg .cluster text{fill:#333;}#my-svg .cluster span,#my-svg p{color:#333;}#my-svg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(78.1578947368, 58.4615384615%, 84.5098039216%);border:1px solid #6eaa49;border-radius:2px;pointer-events:none;z-index:100;}#my-svg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#000000;}#my-svg .node rect{fill:white;}#my-svg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}</style>
6c6
<         <marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="10" viewBox="0 0 12 20" class="marker flowchart" id="flowchart-pointEnd">
---
>         <marker orient="auto" markerHeight="12" markerWidth="12" markerUnits="userSpaceOnUse" refY="5" refX="10" viewBox="0 0 10 10" class="marker flowchart" id="flowchart-pointEnd">
131,133c131,133
<                 <g transform="translate(165.4921875, 237)" id="flowchart-F-26" class="node default default flowchart-label">
<                     <rect height="138" width="115" y="-69" x="-57.5" ry="5" rx="5" style="" class="basic label-container"/>
<                     <g transform="translate(-50, -61.5)" style="" class="label">
---
>                 <g transform="translate(165.4921875, 244.5)" id="flowchart-F-26" class="node default default flowchart-label">
>                     <rect height="153" width="87.84375" y="-76.5" x="-43.921875" ry="5" rx="5" style="" class="basic label-container"/>
>                     <g transform="translate(-36.421875, -69)" style="" class="label">
135c135
<                         <foreignObject height="123" width="100">
---
>                         <foreignObject height="138" width="72.84375">
139c139
<                                     <img src="data:image/svg+xml,%3Csvg
---
>                                     <img style="display: flex; flex-direction: column; width: 100%;" src="data:image/svg+xml,%3Csvg

It looks like the change was in PR https://github.com/mermaid-js/mermaid/pull/4268, or commit 9bb0cef82bba4f31878852c9bf8075964652b93f.

Edit: I've done some testing and confirmed that commit 9bb0cef82bba4f31878852c9bf8075964652b93f is the one that changed this behavior.

Valentine14th commented 1 year ago

Hi,

I'm the one who wrote PR https://github.com/mermaid-js/mermaid/pull/4268. Sorry about this issue. The way the code is supposed to work is to scale the images to the width of the text in the same node by setting width=100% in the node style. This works with most images, however for this svg it seems that it crops it instead of scaling it. I'm not an svg expert but it seems like it is because it doesn't have a viewBox attribute. If you use this slightly modified svg with a viewBox:

<img height='100' width='100' src='data:image/svg+xml,%3Csvg viewBox=%220 0 100 100%22 xmlns=%22http://www.w3.org/2000/svg%22%3E%3Ccircle cx=%2250%22 cy=%2250%22 r=%2240%22 stroke=%22black%22 stroke-width=%223%22 fill=%22red%22 /%3E%3C/svg%3E'/>

the image is not cropped anymore. So

graph TD
      B["fa:fa-car for peace"]
      B-->C[fa:fa-ban forbidden]
      B-->D(fa:fa-spinner);
      B-->E(A fa:fa-camera-retro perhaps?);
      %% Test whether embed <img> work correctly
      D-->F("<img height='100' width='100' src='data:image/svg+xml,%3Csvg viewBox=%220 0 100 100%22 xmlns=%22http://www.w3.org/2000/svg%22%3E%3Ccircle cx=%2250%22 cy=%2250%22 r=%2240%22 stroke=%22black%22 stroke-width=%223%22 fill=%22red%22 /%3E%3C/svg%3E'/> <br/> Red Circle")

renders a

redcircle

I am not sure how big of an issue that is?

aloisklink commented 1 year ago

I'm the one who wrote PR https://github.com/mermaid-js/mermaid/pull/4268. Sorry about this issue.

No worries! The image rendering improvements in PR #4268 seem really useful. To be honest, this might be my fault, I'm not an expert on SVGs, but from some quick googling, it sounds like SVGs without a viewBox have weird behaviors, and some browsers treat them differently.

In fact, adding a viewBox also makes the mermaid diagram work correctly on https://mermaid.live too!

I'm going to close this issue, since it sounds like missing a viewBox in SVGs have strange behaviors in most browsers, but I'm an SVG novice, so I'm happy to re-open this if somebody more knowledgeable about SVGs/browsers chimes in.

If you use this slightly modified svg with a viewBox:

@Valentine14th, do you mind if I submit the change you've recommended to the mermaid-cli project and credit you as a Co-author? I've tested the change, and adding a viewBox works great!

Valentine14th commented 1 year ago

@Valentine14th, do you mind if I submit the change you've recommended to the mermaid-cli project and credit you as a Co-author? I've tested the change, and adding a viewBox works great!

@aloisklink Of course go ahead! Thank you!