javascriptadobesnap.svg

Manipulating an SVG file from Illustrator to change Path names to text within that path


I currently have a file on Illustrator that needs to be manipulated. In the file (SVG), there are many rectangles or 'paths' which are named as such (paths). On top of these paths, there is text which I want to use in order to rename all of the paths within the document to their specific (ID).

enter image description here ID is simply the text that sits on top of the path in the document. Hopefully you are able to see in the image above what I mean.

Currently that text and those boxes are separate, and I need to rename the boxes with the corresponding text that sits on top of them.

I am currently doing this by manually copy and pasting the text and renaming the boxes.

Does anyone know how to write something which would automate this for me?

I am very new to the programming world. I don't even know where to start.

I was thinking a library on JS like Snap.svg would be good?

Any help would be greatly appreciated as this document is very very big (not just that picture) and it takes a long time to do this.

I have tried looking at Snap.svg but due to my lack of experience I dont know where to start. someone with more experience might find this very simple and take them a few minutes to come up with a solution.


Solution

  • Search for the underlying elements via document.elementsFromPoint()

    Keep in mind: svg <text> elements can't be nested within shapes like <rects> – so we can't query for child elements.

    document.elementsFromPoint() returns an array of elements at specific point coordinates and is natively supported by all major browsers - no need for snap.svg or othe libraries.

    1. query for all <text> elements
    2. get center coordinates:
      let bb = label.getBoundingClientRect();
      let x = bb.left + bb.width/2;
      let y = bb.top + bb.height/2;
    3. find elements let els = document.elementsFromPoint(x, y)

    let svg = document.querySelector('svg');
    let labels = document.querySelectorAll('text');
    
    labels.forEach(label => {
      let bb = label.getBoundingClientRect();
      let x = bb.left + bb.width / 2;
      let y = bb.top + bb.height / 2;
      // sanitize id string
      let id = label.textContent.replaceAll(' ', '-').replaceAll('#', '').toLowerCase();
    
      // filter only geometry elements
      let els = document.elementsFromPoint(x, y).filter(el => el instanceof SVGGeometryElement);
    
      // optional: select only first underlying element
      let onlyFirstUnderlying = false;
      if (onlyFirstUnderlying) {
        els = [els[0]];
      }
      els.forEach((el, i) => {
        el.id = id
        // add incremental suffix to prevent duplicate ids
        if (els.length > 1 && i > 0) {
          el.id += '_' + i
        }
      })
      let newSvg = new XMLSerializer().serializeToString(svg);
      output.value = newSvg;
    })
    svg {
      max-width: 10em;
      height: auto;
      border: 1px solid #ccc;
    }
    
    text {
      font-size: 10px
    }
    
    #output {
      display: block;
      width: 100%;
      min-height: 20em;
    }
    <svg id="svg" viewBox="0 0 100 100">
      <rect x="0" y="0" width="50" height="50" fill="yellow" />
      <text x="10" y="25">Label 01</text>
      <rect x="0" y="50" width="50" height="50" fill="orange" />
      <rect x="0" y="50" width="40" height="50" fill="purple" />
      <rect x="0" y="50" width="30" height="50" fill="pink" />
      <text x="10" y="75">Label 02</text>
    </svg>
    <h3>Output</h3>
    <textarea id="output"></textarea>