Question

I have written a script for converting SVG to PNG which is working pretty well, until there is content in the SVG that is "off the page".

eg:

SVG with content that is 'off page'

The black line will be converted fine, but the green box is completely ignored. Some SVG files have the content completely off the page and renders an empty PNG file.

My current command:

convert -background none "$file" -trim -geometry $size "$target"

I have 2k + SVG files I am converting with at least around 500 or more with this issue so manually moving the image onto the page is not an option really.

Edit Here is a link to an example file. The actual drawing is in line with the bottom of the page to the left hand side.

Was it helpful?

Solution 2

So I finally got this working. Could not seem to do it with ImageMagick directly so ended up manipulating the actual SVG data.

First thing is to find the bounds of the object. Using PHP and simpleXml I converted the SVG data to an array for easy manipulation / traversing.

It seems like all the coordinates are in the form of x y so this breaks the coordinates up into numbers and switches between x / y recording if its higher/lower than the previous values.

/** $line is a <g> object */
protected function _position($line, &$position) {
        if(empty($line['@d'])) {
            if(is_array($line)) {
                foreach($line as $l) {
                    self::_position($l, $position);
                }
                return;
            }
        }

        if(empty($position)) {
            $position = array(
                'min' => array(
                    'x' => null,
                    'y' => null
                ),
                'max' => array(
                    'x' => null,
                    'y' => null
                )
            );
        }

        foreach(array_filter(preg_split('/([a-z])/i', $line['@d'])) as $cord) {
            $coordinate = 'x';
            foreach(explode(' ', $cord) as $value) {
                if(empty($value)) {
                    continue;
                }

                if($position['min'][$coordinate] == null || $position['min'][$coordinate] > $value - 1) {
                    $position['min'][$coordinate] = $value;
                }

                if($position['max'][$coordinate] == null || $position['max'][$coordinate] < $value + 1) {
                    $position['max'][$coordinate] = $value;
                }

                $coordinate = ($coordinate != 'x') ? 'x' : 'y';
            }
        }
    }

Once you got the min/max bounds I found a property transform with translate which moves an object by the x,y specified. So just take 0 - $min and it will be moved to 0 as it could be 0 - -10 or 0 - 10

All my files have with no attributes so I just done a simple replace of <g> with <g transform="translate($widthOffset, $heightOffset)">

Also set the viewbox size:

$xml['svg']['@viewbox'] = sprintf('%s %s %s %s',
    $position['min']['x'],
    $position['min']['y'],
    ($position['max']['x'] - $position['min']['x']),
    ($position['max']['y'] - $position['min']['y'])
);

Last thing to do is just convert the array back to XML and save the new data back into the file.

OTHER TIPS

Of course the content that is 'off page' will be ignored in a PNG. Unlike SVG, the PNG file format isn't aware of an 'off page' concept.

This is similar to printing on a piece of paper: the printer also can't display 'off page' content.

There are two options for you to explore. The effect of both is that you'd change the actual page size to a much larger one:

  1. You may be able to get hold of the 'off page' content in the PNG by rendering the SVG to a much bigger PNG canvas, and afterwards automatically trimming the superfluous edges. (I can't try this myself for lack of a sample file.)

  2. You could try to achieve the desired result in a 2- or 3-step process:

    • modify the SVG by increasing its canvas so that it contains all objects 'on page';
    • convert the modified SVG to PNG;
    • (optional: trim the edges of the PNG).
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top