Вычисление ограничительной рамки SVG с использованием PHP относительно кривых

Я недавно нашел этот удивительный класс, расположенный здесь, и попытался использовать его.

Однако он работает только с некоторыми основными функциями, такими как перемещение, горизонтальная линия и вертикальная линия.

-

Я попытался расширить этот существующий класс, добавив дополнительные проверки (и изменив регулярное выражение).

public static function fromPath($pathString) {
    preg_match_all('/([mlvhzc][^mlvhzc]*)/i', $pathString, $commands);
    $pt = array(0, 0);
    $bounds = new self();
    foreach ($commands[0] as $command) {
        preg_match_all('/((\+|-)?\d+(\.\d+)?(e(\+|-)?\d+)?)/i', $command, $matches);
        $i = 0;
        while ($i < count($matches[1])) {
            switch ($command[0]) {
                case 'm' :
                case 'l' :
                    $pt[0] += $matches[1][$i++];
                    $pt[1] += $matches[1][$i++];
                    break;
                case 'M' :
                case 'L' :
                    $pt[0] = $matches[1][$i++];
                    $pt[1] = $matches[1][$i++];
                    $last=$pt;
                    break;
                case 'v' :
                    $pt[1] += $matches[1][$i++];
                    break;
                case 'V' :
                    $pt[1] = $matches[1][$i++];
                    $last[1]=$pt[1];
                    break;
                case 'h' :
                    $pt[0] += $matches[1][$i++];
                    break;
                case 'H' :
                    $pt[0] = $matches[1][$i++];
                    $last[0]=$pt[0];
                    break;
                case 'z' :
                case 'Z' :
                    break;
                case 'c':
                    $pt[0] = $last[0]+$matches[1][4];
                    $pt[1] = $last[1]+$matches[1][5];
                    $last=$pt;
                    $i=count($matches[1]);
                    break;
                default :
                    throw new RuntimeException("Unhandled path command: " . $command[0]);
            }
            $bounds->extend($pt[0], $pt[1]);
        }

    }
    return $bounds;
}

Я проверил руководство по SVG и обнаружил, что "c" имеет только 6 параметров, зная, что последние 2 - это то место, где заканчивается кривая, я попытался расширить точки на основе этого...

На данный момент мои тесты основаны на этом:

<svg xmlns="http://www.w3.org/2000/svg" width="109" height="109" viewBox="0 0 109 109">
<g style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;">
    <path d="M32.25,41c1.25,0.62,3.12,0.67,5.5,0.5c7.12-0.5,19.12-2.5,24-3c0.99-0.1,2.62-0.25,3.75,0" />            
</g>

При запуске в браузере Chrome сообщает, что его отношение ширины к высоте (поскольку я знаю, что svg точно не имеет размеров) составляет около 5-6, однако, когда я нахожу соотношение с помощью своего сценария, оно полностью отключается.

Я хотел бы знать, есть ли другой класс svg, который поддерживает все функции (C,c,Q,q и т. Д.).

Я знаю, что есть способ получить окно, преобразовав его в изображение, но я чувствую, что это неэффективно, также есть getBBox в javascript, но я хотел бы выполнить вычисления на сервере.

Спасибо за прочтение!

2 ответа

Решение

Вот пример использования imagick, на самом деле это два примера в одном, поскольку они не могут быть запущены одновременно, раскомментируйте один за раз:

$svg = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="109" height="109" viewBox="0 0 109 109">
<g style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;">
    <path d="M32.25,41c1.25,0.62,3.12,0.67,5.5,0.5c7.12-0.5,19.12-2.5,24-3c0.99-0.1,2.62-0.25,3.75,0" />            
</g>
</svg>';

$im = new Imagick();
$im->readImageBlob($svg);
$im->trimImage (0);//This trims the unecessary blank space.

//This block gets the dimensions (comment this block before uncommenting the second example bellow)
$dimension = $im->getImageGeometry();
print_r('<pre>');
print_r($dimension);
die();


/*//Uncomment this block to view thw jpeg version of the svg
$im->setImageFormat("jpeg");
header("Content-Type: image/jpeg");
$thumbnail = $im->getImageBlob();
echo $thumbnail;
$im->clear();
$im->destroy();
//*/

Ответ Германа мне очень помог, но на самом деле он не урезал SVG. Ниже первое решение, к которому я пришел, основываясь на его ответе:

function getTrimmedSvg( $filePath )
{
    $image = new Imagick();
    $image->readImage( $filePath );
    $image->trimImage( 0 );

    $imagePage = $image->getImagePage();
    $dimensions = $image->getImageGeometry();

    $minXOut = $imagePage['x'];
    $minYOut = $imagePage['y'];
    $widthOut = $dimensions["width"];
    $heightOut = $dimensions["height"];

    $xml = simplexml_load_file( $filePath );

    $xml["viewBox"] = "$minXOut $minYOut $widthOut $heightOut";

    return $xml->asXML();
}

Хотя это работает большую часть времени, оно не работает все время. После долгих безрезультатных попыток исправить крайние случаи я переключился на использование утилиты командной строки nodejs под названием svg-bounding-box ( GitHub). Это позаботилось обо всех моих крайних случаях. После глобальной установки svg-bounding-box на ваш сервер и / или в среду разработки вы можете использовать его в своем коде следующим образом:

function getTrimmedSvg( $filePath )
{
    $xml = simplexml_load_file( $filePath );

    $xml["viewBox"] = shell_exec( "cat $filePath | svg-bounding-box" );

    return $xml->asXML();
}
Другие вопросы по тегам