Is it possible to delete only the shapes that have only a fill an no stroke?

42 views Asked by At

I would like to keep only the shapes that have no fill on this map : https://commons.wikimedia.org/wiki/File:Antarctica_in_the_World_(yellow).svg Obviously, I don't want to remove all the other ones by hand...

Is it possible to delete only the shapes that have only a fill an no stroke ? Maybe by editing the code of the file? I don't know how to do, but I know how to use regexes if necessary.

Thank you for your help.

1

There are 1 answers

4
herrstrietzel On BEST ANSWER

You might use inkscape's command line but I think a custom java script is more convenient as you can immediately preview the output.

Example 1: parse from XML markup

  1. parse the svg markup
  2. query all paths (optional: include polygons and polylines)
  3. loop: check each path's fill and stroke attribute
    3.1. remove elements without stroke
  4. get updated svg markup via new XMLSerializer()

let markup = `<svg viewBox="0 0 100 30">
  <path  id="is" fill="#CECECE" stroke="none" d="M5.1 8.7l5.4-0.7l1.6-0.5l3.3 0.4l2.1-0.8l3.7 0.1l-2.2 1l2.2-0.4l3.8 0.5l-7.7 2.4l3.7 0.3h3.4l-12.2 1.7l-2.5-0.1l-4.2 0.1l1.4 1.3l6-0.4h3.5l4.1 0.5l-1.5 1.3l1.8 1l5.8-1.6l-2.5 0.8l-0.4 1.3l-2.2 0.7l5.6-0.5l-2.5 1.6l-2.6 0.1l-2.4 3.8l-2.1 0.1l-1-0.5l-0.5 2.1l8.1-0.4l3.8 0.1l3.1-0.8l-0.3 0.8l2.7 0.3l0.1 0.4l3.1-0.7l1 0.9l-1.4 0.3l6.4 1.3l8 1l6-0.3l2.4-0.7l1.3-1.2l3.4-0.8l4.5-1.3l6.3-0.5l7.6-2.9l5.1 0.4l2.9-2.5l1.3-1.6l5.4-2.7l-1.3-0.9l2.4-0.1l-3.6-0.7l5.6-0.4l-3.8-0.9l3.3-0.7l-3.8-0.1l3-2l-4.5-0.8l-3.8 1l2-2.4l-4.6 0.5l2-1.8l-4.6-1.4l1.2-0.9l3.7-1l-7.2 2l-3.1-1.8l-4.8-1l-2.2 0.7l-0.9 2.5l-5-1l-3.8 2.4l-7.1-2l1 1.6l1.2 2.5l-7.7-4.3l-1.7 0.9h-2.6l-0.4 2.5l-6.2-2.7l-2.1 1.8l-1.2 2.4l-2.6-0.6l-3.1 1.6l0.1 2.4l-3.6-2.4l-2.5-2.6l1.8-1.6l-0.5-0.8l-2.5-1.3l-6.4-1.8l-4.3 0.9l6 0.7l-4.5 0.8l4.1 1.4l-1.8 1.3l-3.7-1.3l0.4-0.8l-3.4-0.8l-0.4 0.7l0.4 0.9l-3.8-0.7l4.2 1.7l-4.6 0.3l5 0.7l-2.4 0.3l2.1 0.4l-7.3-0.8l2.2 1l0.4 0.8l-5.5 0.3l5.1 0.7"/>
  
  
<path id="is_1_" fill="none" stroke="#1178AC" d="M5.1 8.7l5.4-0.7l1.6-0.5l3.3 0.4l2.1-0.8l3.7 0.1l-2.2 1l2.2-0.4l3.8 0.5l-7.7 2.4l3.7 0.3h3.4l-12.2 1.7l-2.5-0.1l-4.2 0.1l1.4 1.3l6-0.4h3.5l4.1 0.5l-1.5 1.3l1.8 1l5.8-1.6l-2.5 0.8l-0.4 1.3l-2.2 0.7l5.6-0.5l-2.5 1.6l-2.6 0.1l-2.4 3.8l-2.1 0.1l-1-0.5l-0.5 2.1l8.1-0.4l3.8 0.1l3.1-0.8l-0.3 0.8l2.7 0.3l0.1 0.4l3.1-0.7l1 0.9l-1.4 0.3l6.4 1.3l8 1l6-0.3l2.4-0.7l1.3-1.2l3.4-0.8l4.5-1.3l6.3-0.5l7.6-2.9l5.1 0.4l2.9-2.5l1.3-1.6l5.4-2.7l-1.3-0.9l2.4-0.1l-3.6-0.7l5.6-0.4l-3.8-0.9l3.3-0.7l-3.8-0.1l3-2l-4.5-0.8l-3.8 1l2-2.4l-4.6 0.5l2-1.8l-4.6-1.4l1.2-0.9l3.7-1l-7.2 2l-3.1-1.8l-4.8-1l-2.2 0.7l-0.9 2.5l-5-1l-3.8 2.4l-7.1-2l1 1.6l1.2 2.5l-7.7-4.3l-1.7 0.9h-2.6l-0.4 2.5l-6.2-2.7l-2.1 1.8l-1.2 2.4l-2.6-0.6l-3.1 1.6l0.1 2.4l-3.6-2.4l-2.5-2.6l1.8-1.6l-0.5-0.8l-2.5-1.3l-6.4-1.8l-4.3 0.9l6 0.7l-4.5 0.8l4.1 1.4l-1.8 1.3l-3.7-1.3l0.4-0.8l-3.4-0.8l-0.4 0.7l0.4 0.9l-3.8-0.7l4.2 1.7l-4.6 0.3l5 0.7l-2.4 0.3l2.1 0.4l-7.3-0.8l2.2 1l0.4 0.8l-5.5 0.3l5.1 0.7"/>
</svg>`;

// parse svg from raw xml markup
let svg = new DOMParser()
  .parseFromString(markup, "text/html")
  .querySelector("svg");

// query all paths and similar elements
let paths = svg.querySelectorAll("path, polygon, polyline");
paths.forEach((path) => {
  // check attributes
  let stroke = path.getAttribute("stroke");
  let fill = path.getAttribute("fill");
  
  // remove if element doesn't have stroke but a fill
  if ((fill || fill!='none') && (!stroke || stroke==='none') ) {
    path.remove();
  }
});

// get updated svg
let markupNew = new XMLSerializer().serializeToString(svg)
console.log(markupNew)


// render preview
preview.append(svg);
svg{
  border:1px solid #ccc;
  overflow:visible
  width: 50%;
  height: auto;
}
<div id="preview"></div>

Example 2: parse from XML fetched (remote) svg file

This approach requires the svg source to allow cross origin access. So the svg needs to have a appropriate CORS header or to be served from same domain.

/**
 * fetch data asynchronously
 */
(async() => {
  let url = "https://upload.wikimedia.org/wikipedia/commons/b/b6/Antarctica_in_the_World_%28yellow%29.svg"
  let fetchedData = await (fetch(url));
  let markup = await fetchedData.text()
  // remove tabs and newlines
  markup = markup.replaceAll('\t', '').replace(/[\n\r\t]/g, "")

  // parse svg
  let svg = new DOMParser().parseFromString(markup, 'text/html').querySelector('svg')

  /**
   * cleanup:
   * remove AI metadata 
   * and forreignObjects
   */
  let remove = svg.querySelectorAll('foreignObject, #adobe_illustrator_pgf')
  remove.forEach(el => {
    el.remove()
  })

  // unwrap switch
  let switchEl = svg.querySelector('switch');
  let switchCnt = switchEl.children[0]
  switchEl.parentNode.insertBefore(switchCnt, switchEl)
  switchEl.remove()

  // query all paths and similar elements
  let paths = svg.querySelectorAll('path, polygon, polyline')
  paths.forEach(path => {
    // check attributes

    let stroke = path.getAttribute('stroke')
    let fill = path.getAttribute('fill')
    // remove if element doesn't have stroke but a fill
    if ((fill || fill != 'none') && (!stroke || stroke === 'none')) {
      path.remove()
    }
  })

  // remove empty groups
  let groups = svg.querySelectorAll('g')
  groups.forEach(g => {
    if (!g.querySelectorAll('path, polygon, rect, circle, ellipse, line, polyline, text').length) {
      g.remove()
    }
  })


  // render preview
  preview.append(svg)

  // cleanup whitespace and create download
  let markupNew = new XMLSerializer().serializeToString(svg)
    .replace(/\ {2,}/g, " ")
    .replace(/[\n\r\t]/g, "")
    .replaceAll('>', '>\n')

  let blob = new Blob([markupNew], {
    type: 'image/svg+xml'
  })
  output.value = markupNew;
  fileSize.textContent = +(blob.size / 1024 / 1024).toFixed(3) + ' KB'
  btnDownload.href = URL.createObjectURL(blob)


})()
svg {
  width: 100%;
  height: auto;
}

textarea {
  width: 100%;
  min-height: 10em;
  display: block;
}
<p><a id="btnDownload" href="" download="new.svg">Download</a> <span id="fileSize"></span> </p>
<div id="preview"></div>
<h3>Output</h3>
<textarea id="output"></textarea>

How it works

  1. we're fetching the svg file
  2. parse it's markup via new DOMParser()
  3. query all path and polygon elements (quite often shapes in maps can either be paths or polygons)
  4. loop through all path elements and check their fill and stroke attributes via element.getAttribute(): if a path has a fill but no stroke attribute we can remove this item
  5. create a blob and an object URL to add a download link (doesn't work in SO snippets)

Cleanup (optional)

Besides, we can remove some proprietary Adobe Illustrator metadata (used for preview images, editor settings and assets etc.).
We can also remove empty groups or unused definitions.
This way we can significantly reduce file size.

Testing: codepen example