makeSvg method is not rendering image under it

Hi,
My Diagram have some images, while i am doing makeSvg, these images are not coming.
I have also gone through the following issue:

But here also the images are missing if I pasted the svg element in some other page
I think this is due to imgsrc/path_of_Image is different in other page:

Please find Below Code I am using for Downloading SVG file:

const filename = 'newSvg.svg';
const aTag = document.createElement('a');
const svg = diagram.makeSvg({ scale: 1, background: 'white' });
const svgstr = new XMLSerializer().serializeToString(svg);
const blob = new Blob([svgstr], { type: 'image/svg+xml' });
document.body.appendChild(aTag);
const url = window.URL.createObjectURL(blob);
aTag.setAttribute('href', url);
aTag.setAttribute('download', filename);
aTag.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(aTag);

Is there a method to accommodate these embedded images

Are all of the images visible in the diagram before you execute that call to Diagram.makeSvg?

You can look at the resulting SVG XML to see if the references are correct.

If you want it to wait some time for any unloaded Pictures to be loaded, use the callback and callbackTimeout options.

Yes All of the Images are visible before calling Diagram.makeSvg

<image x="0" y="0" width="66" height="66" xlink:href="assets/images/task-map/decision-box.svg" preserveAspectRatio="none" transform="matrix(1, 0, 0, 1, 0, 0) scale(0.8667272727272728, 0.8667272727272728)"/>

This is the image tag inside the svg and xlink:href is correct
Infact I have also tried given relative path:
xlink:href="../../../../assets/images/task-map/decision-box.svg"

Use the elementFinished option to Diagram | GoJS API to modify that SVG image element to use the desired URL.

Or maybe you want to embed the SVG rather than refer to the image. See SVG Images using Data URL GoJS Sample

As Suggested I have done the following changes, but still it doesn’t work,
On Debugging I found that all the elementFinished calls are done after my file is downloaded.
Is there any way where I can call download file code once all the elementFinished calls are done. Something like makeSvg Finish or something…?

exportSvg = ()=>{
    const filename = 'new.svg';
    const aTag = document.createElement('a');
    const svg = diagram.makeSvg({
        scale: 1,
        background: 'white',
        elementFinished: (graphobject, svgelement) => {
            if (!(graphobject instanceof go.Picture)) return;
            toDataURL(svgelement.href.baseVal, (dataUrl) => {
                svgelement.setAttribute('href', dataUrl);
            });
        },
    });
    const svgstr = new XMLSerializer().serializeToString(svg);
    const blob = new Blob([svgstr], { type: 'image/svg+xml' });
    document.body.appendChild(aTag);
    const url = window.URL.createObjectURL(blob);
    aTag.setAttribute('href', url);
    aTag.setAttribute('download', filename);
    aTag.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(aTag);
}
toDataURL = (url, callback)=>{
    const xhr = new XMLHttpRequest();
    xhr.onload = () => {
        const reader = new FileReader();
        reader.onloadend = () => {
            callback(reader.result);
        };
        reader.readAsDataURL(xhr.response);
    };
    xhr.open('GET', url);
    xhr.responseType = 'blob';
    xhr.send();
}

I believe the elementFinished option function is called synchronously while building the SVG DOM.
Your toDataURL function is causing asynchronous behavior.
Maybe you need to use the callback option of Diagram.makeSvg.

The SVG that you are drawing is already present in memory, so I do not see why you need to reload the SVG URL again.

We don’t have callback option on Diagram.makeSvg . right…?
I was referring following page :

Please Let me know if there is a way to use a callback.
As for testing when i gave a delay of 100 ms it was working,
but cant use the delay as for bigger Diagram might take more than 100 ms.

You have to wait until the SVG has finished downloading and converting the images to data URLs and modified the SVG DOM before you can begin the download. So you cannot depend on the return value from makeSvg, but must get it from the callback function.

Ya That is fine but makeSvg Method do not have a callback function right?

It’s documented. Did you have a problem when you tried it?

Yes It was giving the following error:

Argument of type '{ scale: number; background: string; elementFinished: (graphobject: GraphObject, svgelement: SVGElement) => void; callback: () => void; }' is not assignable to parameter of type 'SvgRendererOptions'.
  Object literal may only specify known properties, and 'callback' does not exist in type 'SvgRendererOptions'.

and even at Diagram | GoJS API callback is given under Additional image-specific arguments (not for SVG)

You’re right about the documentation! We should fix that.

I did a quick test, calling myDiagram.makeSvg({ scale: 1, callback: function(svg) { console.log(svg); } }). It worked as I expected, returning null immediately and then calling the callback with the SVG DOM.

I also did a quick test:

<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="https://unpkg.com/gojs@2.1.21/release/go.js"></script>
<script id="code">
  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv");

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        $(go.Shape,
          { fill: "white" },
          new go.Binding("fill", "color")),
        $(go.Picture,
          { margin: 8, width: 50, height: 50 },
          new go.Binding("source", "key", function(t) { return "https://unpkg.com/gojs@2.1.21/samples/images/hs" + t + ".jpg"; }))
      );

    myDiagram.model = new go.GraphLinksModel(
    [
      { key: 1, text: "Alpha", color: "lightblue" },
      { key: 2, text: "Beta", color: "orange" },
      { key: 3, text: "Gamma", color: "lightgreen" },
      { key: 4, text: "Delta", color: "pink" }
    ],
    [
      { from: 1, to: 2 },
      { from: 1, to: 3 },
      { from: 2, to: 2 },
      { from: 3, to: 4 },
      { from: 4, to: 1 }
    ]);
  }

  // partly taken from samples/svgDataUrl.html
  function toDataURL(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = () => {
      var reader = new FileReader();
      reader.onloadend = () => callback(reader.result);
      reader.readAsDataURL(xhr.response);
    };
    xhr.open('GET', url);
    xhr.responseType = 'blob';
    xhr.send();
  }

  function setupDownload(svg) {
    const filename = 'new.svg';
    const aTag = document.createElement('a');
    const svgstr = new XMLSerializer().serializeToString(svg);
    const blob = new Blob([svgstr], { type: 'image/svg+xml' });
    document.body.appendChild(aTag);
    const url = window.URL.createObjectURL(blob);
    aTag.setAttribute('href', url);
    aTag.setAttribute('download', filename);
    aTag.click();
    window.URL.revokeObjectURL(url);
    document.body.removeChild(aTag);
  }

  // Make SVG, but modify the SVG <image> Element's href to refer to a Base64 URI instead of the go.Picture source URL.
  function downloadSvgWithData() {
    myDiagram.makeSvg({
        scale: 1,
        background: 'white',
        elementFinished: (graphobject, svgelement) => {
            if (!(graphobject instanceof go.Picture)) return;
            toDataURL(svgelement.href.baseVal, (dataUrl) => {
                svgelement.setAttribute('href', dataUrl);
            });
        },
        callback: (svg) => { setTimeout(() => { setupDownload(svg); }, 1000); },
    });
  }
</script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <button onclick="downloadSvgWithData()">Download</button>
</body>
</html>

The images were correctly embedded as data URIs in the downloaded SVG.

The use of a 1 second wait isn’t reliable, though, so you will need to decide on your own timeout or else do a better job making sure as many SVG files are downloaded as you can before you give up and download anyway.

Without setTimeout it is not working.
And with setTimeout for Bigger Diagram it breaks.

In-fact if I use setTimeout without callback, then also it is working but for Bigger Diagram we may not have a proper delay duration.
I was expecting that with callback i wont be needed to use setTimeout

The toDataURL function requires asynchrony that Diagram.makeSvg or Diagram.makeImage cannot know about.

The right thing to do is to wait for all of the results from the calls to toDataURL, but that would be easier to do using Promises.