Объединение сложных SVG-файлов с помощью paper.js

#javascript #svg #paperjs

#javascript #svg #paperjs

Вопрос:

Я пытаюсь объединить относительно сложные SVG, подобные этому. Я хочу объединить пути, которые образуют буквы «ПРИВЕТ», с большой буквой «А» позади. В Inkscape я могу сделать это, выбрав все пути и перейдя к Path-> Union , чтобы пути были преобразованы из этого:

введите описание изображения здесь

Для этого:

введите описание изображения здесь

Обратите внимание, как пути, образующие буквы «ПРИВЕТ» в центре, теперь объединены с большой буквой «А» позади них. Я хочу добиться того же, используя paper.js , поскольку это лучшее решение, которое я смог найти, для выполнения логических операций с файлами SVG без их полигонизации и потому, что я не могу сделать это с помощью Inkscape CLI без GUI.

Я создаю этот эскиз, paper.js чтобы загрузить файл SVG и объединить все CompoundPaths и Paths , надеясь, что это приведет к тому же эффекту, но, видимо, это не так просто, результирующий путь из эскиза:

 <path xmlns="http://www.w3.org/2000/svg" d="M-177.63334,-177.64819h755.85598v755.85598h-755.85598z" id="path32" fill="#000000" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"/>
  

Это почти пустой путь! Есть ли какой-нибудь известный способ добиться этого? Я думал о любом способе обнаружения фигур, как это делает Inkscape, я имею в виду, что в Inkscape вы можете индивидуально выбирать и манипулировать каждой буквой как отдельной фигурой, поэтому, если я смогу распознать каждую фигуру (или замкнутый путь?) Я мог бы выполнить операцию объединения между ними.

Я немного не понимаю, как этого добиться, поэтому любые алгоритмы, советы, книги, блоги, код или что-либо еще будут очень оценены!

Ответ №1:

Наконец-то мне удалось это решить! Я должен был убедиться, что не unite указаны пути, которые использовались как clipMask , И убедиться, что все пути были закрыты с помощью closePath() метода. Окончательный код выглядит следующим образом:

 var canvas = document.getElementById('canvas')
var scope = paper.setup(new paper.Size(200, 200))

var svg = `{put your svg string here}`;

paper.project.importSVG(svg, function(pathItem) {
    // Get Path and CompoundPath which are children from this item
    let paths = pathItem.getItems({
        className: 'Path'
    });
    let compoundPaths = pathItem.getItems({
        className: 'CompoundPath'
    });

    // Filter paths that are inside CompoundPaths
    paths = paths
        .filter((p) => !compoundPaths.some((cp) => cp.children.includes(p)))
        // Filter paths that are used as clipping paths
        .filter((p) => !p.clipMask);
    compoundPaths = compoundPaths.filter((c) => !c.clipMask);

    // Close all paths to ensure a correct union
    for (const path of compoundPaths.filter((c) => !c.closed)) {
        path.closePath();
    }
    for (const path of paths.filter((c) => !c.closed)) {
        path.closePath();
    }

    // If not paths or compound paths are available, return the same input SVG
    if (!paths.length amp;amp; !compoundPaths.length) {
        debugger;
    }
    else {
        // Merge all the paths to build a single path
        let unitedItem = undefined;
        let compoundPathsStartIndex = 0;
        if (paths.length) {
            unitedItem = paths[0];
            for (let n = 1; n < paths.length;   n) {
                const path = paths[n];
                unitedItem = unitedItem.unite(path);
            }
        } else {
            unitedItem = compoundPaths[0];
            compoundPathsStartIndex = 1;
        }

        for (let n = compoundPathsStartIndex; n < compoundPaths.length;   n) {
            const path = compoundPaths[n];
            unitedItem = unitedItem.unite(path);
        }

        // Set fill color otherwise paths exported in the server (which uses node 8) end up without
        //  a filling color
        unitedItem.fillColor = new paper.Color(0, 0, 0);

        // Generate the merged SVG string and save it
        const outputPathString = unitedItem.exportSVG({
            asString: true,
            bounds: new paper.Rectangle(0, 0, pathItem.getBounds().width, pathItem.getBounds().height)
        });
        // let outputSvg = outputPathString;
        let outputSvg = `<?xml version="1.0" encoding="utf-8"?>n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="${pathItem.getBounds().width}" height="${pathItem.getBounds().height}">`;
        outputSvg  = outputPathString;
        outputSvg  = '</svg>';
        
        console.log(outputSvg);
        debugger;
    }
});
  

Полный код с включенной строкой SVG находится здесь, поскольку я превышаю максимальную длину тела, включая его в ответ.