Search code examples
svgcommand-linerotationimagemagickmogrify

convert or mogrify to rotate an svg file on command line : "delegate failed" error


I want to rotate an image in an svg file by x degrees in the very straightforward way, on the command line (not via a gui program), such that if a picture were on your kitchen table the picture would be simply turned around to face to the side, or where ever - i.e. real straightforward (one hopes).

[ADDED INFO]: the svg file is from gnuplot multiplot mode, in particular - but of course gnuplot has many terminals. I say this because multiplot appears unable to rotate the computed image by 90 (in the "do what I mean" sense - even though the statement in plain English does not make sense ); however, multiplot can do that to rgb files - see the famous tutorial page with Tux images.

If an svg file is given to convert to rotate by 90 degrees (for instance) :

convert -rotate 90 test_start.svg test_plus_90.svg

stderr (I guess) is reported:

convert-im6.q16: delegate failed `'potrace' --svg --output '%o' '%i'' @ error/delegate.c/InvokeDelegate/1966.

no output is produced and the original file is unchanged. if mogrify is used instead, the same error plus this one is issued [ UPDATE: this has to do with the output file, but I'll leave this as it is trivial]:

mogrify-im6.q16: unable to open image `test_plus_90.svg': No such file or directory @ error/blob.c/OpenBlob/2924.

the original file is then appended with a tilde, as test_start.svg~, but appears unchanged.

png and jpg files will process this way fine. It appears that some resources need to be installed, as the "Delegates" for convert do not include svg - or this is the incorrect way to process svg files, I am wrong, etc.

version and system information:

Ubuntu 22, synaptic used to install everything.

ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25 Delegates do NOT include svg, so this appears to be what I need to understand. gimp version 2.10.30


Solution

  • tl;dr You are better off first rendering your SVG image as a raster image, with the final dimensions for display, and only then flipping it around. Or, depending on your use case, quoting the image in a reference and rotating the result.

    Flipping a SVG image by 90° is not a straightforward task, if you want the result to be still in SVG format. At least if you tried to write a tool that covered all the necessary bases for it not to produce seriously botched pictures. Let me give you an example:

    <svg xmlns="http://www.w3.org/2000/svg" width="150vh" height="100vh" viewBox="0 0 400 200">
      <rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
      <circle r="10" cx="5%" cy="50%" fill="#00f" />
      <circle r="10" cx="50%" cy="50%" fill="#00f" />
      <circle r="10" cx="95%" cy="50%" fill="#00f" />
    </svg>

    How would you change that file to flip it around? You would exchange the width and height attributes on the root element, equally the corresponding values in the viewBox attribute. (If there was a preserveAspectRatio attribute, it would also have to be converted, but I'll leave that out.) Then, the content inside has also to be rotated by 90°. Wrap all content in a <g> element with an appropriate transform attribute:

    <svg xmlns="http://www.w3.org/2000/svg" width="100vh" height="150vh" viewBox="0 0 200 400">
      <g transform="rotate(90, 100,100)">
        <rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
        <circle r="10" cx="5%" cy="50%" fill="#00f" />
        <circle r="10" cx="50%" cy="50%" fill="#00f" />
        <circle r="10" cx="95%" cy="50%" fill="#00f" />
      </g>
    </svg>

    Oops. What happened? The cx/cy values of the circles are percentages of width and height of the viewBox, which are already exchanged. But then the rotation is applied, and axis directions for the purpose of positioning are at odds.

    This is obviously not the way to go about it. A conversion tool would have to sift through the complete code looking for these sort of pitfalls and correcting attributes. And let me assure you, I can think of a lot of tricky constructs that would trip implementers.

    Here is a second attempt. <svg> elements can be nested, and they encapsulate their viewport completely. Instead of changing content, wrap the root element in another one and flip the now inner <svg> as a whole.

    But there is a pitfall to avoid: A construct like

    <svg ...>
      <svg transform="rotate(90)" ...>
    

    is only part of the SVG 2 specification. While browsers mostly have implemented that, other renderers may still disregard the transform attribute on the inner <svg>, as prescribed by the SVG 1.1 spec.

    Thus you need to introduce an extra <g transform="..."> element:

    <svg xmlns="http://www.w3.org/2000/svg" width="100vh" height="150vh" viewBox="0 0 200 400">
      <g transform="rotate(90 100,100)">
        <svg width="400" height="200" viewBox="0 0 400 200">
          <rect x="1" y="1" width="398" height="198" fill="#ccc" stroke="#000" />
          <circle r="10" cx="5%" cy="50%" fill="#00f" />
          <circle r="10" cx="50%" cy="50%" fill="#00f" />
          <circle r="10" cx="95%" cy="50%" fill="#00f" />
        </svg>
      </g>
    </svg>

    Well, it seemed to work. Note that the width/height attributes on the inner <svg> needed to be changed. (I assume there is a valid strategy even if one or two of the sizing attributes are missing in the original file, but haven't tested them all.) But imagine a tool flipping the image multiple times. Would it wrap the previous version each time with new outer elements? Would it try to analyze the structure and fold possible multiple applications of flipping into one? Most developers would probably cringe instead of writing such a tool.

    This is not to say that one like this does not exist, But I don't know about it. It is so much easier to change your workflow: stall the flipping to the moment when you render the SVG image to a raster format, and do the flipping afterward and on-the-fly, where it is an easy task.

    If you are using the SVG as an image in other content, it is equally easy to combine rotating with referencing. For example in HTML/CSS:

    <img src="myImage.svg" 
         style="width:...;height:...;transform:rotate(90deg);transform-origin:...">