Error opening svg file from Observable

I used Inputs.table and downloaded svg-file of my chart/table within this tutorial: Saving SVG / Mike Bostock / Observable

My file can be opened in browser, but when I try to open it with Illustrator, I’ve got this error: The operation cannot be complete because of an unknown error. [STEX].
It’s everything Ok with svg file from tutorial though.

Built-in option doesn’t offer to me to save in svg (

How to fix it? Or if there is another way to export chart to svg?

Link to my notebook

1 Like

The cell you are trying to download as SVG is not SVG but HTML. The built in tool for downloading properly recognize this and doesn’t offer you the option. The code referenced by the button doesn’t have this check, attempts to convert the HTML, and returns gibberish. In short, there’s no reason to expect those tools to work.

Here’s a work around that might be sufficient for your needs:

There are a number of options for that last step. I used Inkscape but I’d guess that you’d want to use Illustrator.

Here’s what the resulting SVG looks like after being uploaded and re-displayed in a notebook:

1 Like

I took a quick stab at using dom-to-svg. It’s still failing to inline those external SVGs, so the flags are missing:

Here’s the code:

{
  const {elementToSVG, inlineResources} = await import('https://esm.sh/dom-to-svg');
  
  // Capture specific element
  const {documentElement: svg} = elementToSVG(graph);

  // Seems to ignore xlink:href. Replace with href.
  for(const n of svg.querySelectorAll('[xlink\\:href]')) {
    n.setAttribute('href', n.getAttribute('xlink:href'));
    n.removeAttribute('xlink:href');
  }
  // Inline external resources (fonts, images, etc) as data: URIs
  await inlineResources(svg);

  // Detach from its parent, so that Observable's Inspector will display it.
  svg.remove();
  return svg;
}
2 Likes

We can inline the SVGs, but they conflict with each other due to duplicate IDs:

One way would be to just rasterize them, i.e., render them to a canvas and inline that as data URL. Frankly I’m surprised that dom-to-svg can’t handle that already – might wanna take a look at the issues and docs.

The other way would be to prefix the IDs, e.g. with SVGO: Hello, SVGO! / Fabian Iwand / Observable

Again, the code for the above image:

{
  const {elementToSVG, inlineResources} = await import('https://esm.sh/dom-to-svg');
  
  // Capture specific element
  const {documentElement: svg} = elementToSVG(graph);

  for(const n of svg.querySelectorAll('image[xlink\\:href$=".svg"]')) {
    const markup = await fetch(n.getAttribute('xlink:href')).then(r => r.text());
    const svg = new DOMParser().parseFromString(markup, 'image/svg+xml').documentElement;
    for(const attr of ['x', 'y', 'width', 'height']) {
      if(n.hasAttribute(attr)) svg.setAttribute(attr, n.getAttribute(attr));
    }
    n.replaceWith(svg);
  }
  // Inline external resources (fonts, images, etc) as data: URIs
  await inlineResources(svg);

  // Detach from its parent, so that Observable's Inspector will display it.
  svg.remove();
  return svg;
}
1 Like

Seems like good option, if only not issue with flags, but still has with it the same error btw (((

Without bars (((

1 Like

I didn’t even notice that! I had to fiddle with the style and save the embedded copy from Chrome but the new SVG should work fine:

2 Likes

That’s great, how can I find code for future using?