Search code examples
javascriptsvgsplitcut

Split SVG into pieces - Javascript


I have a big svg tag, with lots of svg polygons, line, text inside it making a 2D map, which need overflow to see it full size on screen, something like that:

enter image description here

I need a way to print it from broswer when I click "print" or use "ctrl + p", but for that i need to break it into pieces and put then on column layout, so they can fit on A4 size to print the entire content, something like that:

enter image description here

When I try to print i get this:

enter image description here

So, I need a way to break this svg field into pieces to fit the page to print. Is there any way to do that, using js, css, anything? Thank you!


Solution

  • There is no way to do what you want with pure CSS.

    You'll need Javascript to create the split sections of the SVG.

    Here's some demonstration code. I've left comments in the code to explain how it works.

    The example uses a checkbox to simulate "print mode" but you could run the split and unsplit functions automatically, when printing, by listening to the beforeprint and afterprint events.

    https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeprint

    function  splitSVGs(splitWidth) {
      let splittables = document.querySelectorAll(".splittable");
      splittables.forEach(function(svgElem) {
    
        // Get starting size of the original SVG now
        const computed = getComputedStyle(svgElem);
        const width = parseInt(computed.width, 10);
        const height = parseInt(computed.height, 10);
        const vB = svgElem.viewBox.baseVal;
        // Get the viewBox of the SVG also
        const bbox = (svgElem.getAttribute("viewBox") !== null) ? {x:vB.x, y:vB.y, width:vB.width, height:vB.height}
                                                                : {x:0, y:0, width, height};
        // Hide the original SVG
        svgElem.classList.add("hide");
        
        // Create a temporary div element to hold our generated sections
        const div = document.createElement("div");
        div.classList.add("sections");
        svgElem.insertAdjacentElement('afterend', div);
    
        let remainingWidth = width;
        while (remainingWidth > 0) {
          const sectionWidth = Math.min(splitWidth, remainingWidth);
          // Convert sectionWidth relative to viewBox
          bbox.width = sectionWidth * bbox.height / height;
    
          // Create an SVG element to contain one section of the split
          const section = document.createElementNS("http://www.w3.org/2000/svg", "svg");
          section.setAttribute("width", sectionWidth);
          // Add a viewBox that shows the area of the original that we want to see in this section
          section.setAttribute("viewBox", [bbox.x, bbox.y, bbox.width, bbox.height].join(' '));
          
          // Add a <use> element to the section SVG that references the original
          const use = document.createElementNS("http://www.w3.org/2000/svg", "use");
          use.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", '#'+svgElem.id);
          use.setAttribute("width", vB.width);
          use.setAttribute("height", vB.height);
          section.appendChild(use);
          
          // Add this section SVG to the sections div
          div.appendChild(section);
          // How much of the original SVG width is left?
          remainingWidth -= splitWidth;
          // Update bbox so the next SVG will show the next section of the original
          bbox.x += bbox.width;
        }
    
      });
      
    }
    
    
    function unsplitSVGs() {
      // Get rid of the generated sections
      const sections = document.querySelectorAll(".sections");
      sections.forEach(function(div) {
        div.remove();
      });
      
      // Unhide all the original SVGs
      const splittables = document.querySelectorAll(".splittable");
      splittables.forEach(function(svgElem) {
        svgElem.classList.remove("hide");
      });
      
    }
    
    
    document.getElementById("print-mode").addEventListener("change", function(evt) {
      if (evt.target.checked) {
        splitSVGs(600);
      } else {
        unsplitSVGs();
      }
    });
    svg {
      background: linen;
    }
    
    svg#test {
      width: 2960px;
      height: 80px;
      border: solid 2px black;
    }
    
    /* Hide while still keeping the contents visible to our section SVGs */
    .hide {
      position: absolute;
      top: -9999px;
    }
    
    .sections svg {
      border: solid 2px black;
    }
    
    .sections svg:not(:first-child) {
      border-left: dashed 2px black;
    }
    
    .sections svg:not(:last-child) {
      border-right: dashed 2px black;
    }
    <p>
    <input type="checkbox" id="print-mode">
    <label for="print-mode"> Simulate print mode (split the SVG)</label>
    </p>
    
    <svg viewBox="0 0 1480 40" id="test" class="splittable">
      <text x="10" y="30" font-size="30px">We choose to go to the Moon in this decade and do the other things, not because they are easy, but because they are hard.</text>
    </svg>