🏠 back to Observable

Control size when "Download PNG"

Is there a way to control density of the PNG produced from an SVG from the “Download PNG” choice ?

I have also noticed that the SVG is not correctly display in Inkscape (size of some rectangle are smaller).

Tested from : Untitled / Patrick Brockmann / Observable

Hello,

I’ve tested the svg in Illustrator, Inkscape and Affinity Designer. I’m not sure of what you’re seeing; for me the difference is that the texts at the left and right have an added rect outline in inkscape (which I suppose shouldn’t show, since it has visibility: hidden;).

inkscape

affinity designer

illustrator

Re: the main question, I don’t know of a way to do that.

On windows with Inkscape 1.0.2 I get this:


Text seems to not follow the changed styling.

1 Like

Ah… maybe the fonts need to be specified differently.

1 Like

CSS and webfont support is spotty in editors. See also

1 Like

Thanks to all for your feedbacks and tests.
Yes, I was very surprised to discover that the rendering of the SVG image is not correct on Inkscape (1.0.2). The first and last colums shoud not have a rectangle around the text (opacity at 0).

In fact I could be satisfied with the proposed PNG file from Observable since the rendering is correct
but is there a way to control its size while this image is produced from the SVG code ?
I need an image with a bigger definition to be included in a report.

1 Like

Yes I’m sending you a suggestion, based on Canvas Plot / Fil / Observable

Observable uses the following heuristic to determine the size of the PNG:

  1. If both width and height attribute are set, use both.
  2. Else, if a valid viewBox attribute is set, use width and height from the viewBox.
  3. Else determine the display size of the SVG and use that.

The resulting width and height values are multiplied by the devicePixelRatio.

Source:

              let n = +e.getAttribute("width")
                , r = +e.getAttribute("height");
              n && r || ({width: n, height: r} = e.viewBox.baseVal),
              n && r || ({width: n, height: r} = e.getBoundingClientRect());
              const i = window.devicePixelRatio || 1
                , a = document.createElement("canvas");
              a.width = n * i,
              a.height = r * i;
1 Like

Interesting to know.
Is there a way to control the devicePixelRatio from an attribute ?

No. The only way to change it is by zooming the page. I advise against any attempts to override it.

Instead I would recommend to provide your own download link, which might be what @fil suggested.

Ideally though Observable would use viewBox to determine the aspect ratio of the SVG, and test for width/height to determine the desired output size.

In my GIF converter notebook, I used the following logic to determine the output size:

function getSVGDimensions(svg) {
  const {width: vw, height: vh} = svg.viewBox.baseVal,
        w = parseVal(svg.width),
        h = parseVal(svg.height),
        // viewBox aspect ratio.
        va = vw > 0 && vh > 0 ? vw / vh : 0,
        // SVG aspect ratio.
        a = w > 0 && h > 0 ? w / h : 0;

  if(va > 0) {
    if(a > 0) return a > va ? [h * va, h] : [w, w / va];
    if(w > 0) return [w, w / va];
    if(h > 0) return [h * va, h];
    return [vw, vh];
  }
  
  if(a > 0) return [w, h];
  if(w > 0) return [w, w];
  if(h > 0) return [h, h];
  
  return [SVG_DEFAULT_SIZE, SVG_DEFAULT_SIZE];
  
  function parseVal({baseVal: v}) {
    try {
      switch(v.unitType) {
        case SVGLength.SVG_LENGTHTYPE_NUMBER:
        case SVGLength.SVG_LENGTHTYPE_PX:
          return v.value;
      }
    }
    catch(e) {}
    return 0;
  }
}
2 Likes

Here are my modifications to get what I wanted (same display in inkscape)

  • use opacity style rather than visibility style to hide some svg rect
  • use an existing font from my system (ie “Arial” for me). The rectangles beside the text appear then the same way in Observable and in Inkscape

Then exporting to a png image can be done from inkscape by controlling the density.

Thanks to all for your expertise.

1 Like

Thanks for those fix tips @PBrockmann I know they will be useful to others (like myself) with SVG to inkscape render issues.

@mootari @Fil would it be possible to add a button to use the observable PNG save API with custom size parameters? And is there an example of that? Great tips too btw, thanks

The other route is to change the SVG size (by changing width, height and scale, or viewBox and scale): if you make it 4x bigger the native “download png” will return a 4x bigger png.

1 Like

Yes indeed but some set ups (font size, width of rectangle nodes, thickness of stroke for labels) have to be set again. So not so seamless.
I have found easier to use density parameter from inkscape.

1 Like

On your notebook I think if you do the following it should work with no need for further adjustments:

    .attr("viewBox", [0, 0, 2 * params.svg.width, 2 * params.svg.height])
    .style("transform", "scale(2,2)")
    .style("transform-origin", "0 0")
2 Likes

Yes, that is cleaver and elegant.

Thks