Alexandre Bento
Alexandre Bento

Reputation: 35

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!

Upvotes: 0

Views: 3864

Answers (1)

Paul LeBeau
Paul LeBeau

Reputation: 101820

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>

Upvotes: 3

Related Questions