0

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!

Alexandre Bento
  • 35
  • 1
  • 10
  • Possibly use a headless browser and take screenshots? – Rounin Oct 08 '19 at 12:59
  • We need more information. Are you talking about displaying it in three pieces on a web page, so you can print from the browser? – Paul LeBeau Oct 08 '19 at 13:05
  • Based on the limited information provided: I would look at print css `@media print {` and place the image 3 times vertically. Top image is the left 1/3 of the whole image. Middle image is the middle 1/3 of the whole image and the bottom image is the right 1/3 of the whole image. – Rob Moll Oct 08 '19 at 13:10
  • Paul LeBeau, exactly that, i have this svg which is a 2D map for my company and I need to print it from browser and for that need to split it so it can fit a A4 paper – Alexandre Bento Oct 08 '19 at 13:12
  • Rob Moll is exacly that what i need, but how I do that on @media print, how to get pieces of a svg to display it? – Alexandre Bento Oct 08 '19 at 13:12
  • Are the images consistently sized? Always the same height and width? – Rob Moll Oct 08 '19 at 13:22
  • 1
    I doubt you will find a generic way to split like that with JS. Perhaps converting to an image via canvas and splitting could work but might be unreliable, large libs required etc. I think you'd be better off preparing 3 SVGs and switching to them using media queries in CSS or JS – Dominic Oct 08 '19 at 13:25
  • @Dom - I agree. That is why I asked if the sizes were consistent. – Rob Moll Oct 08 '19 at 13:27
  • Rob Moll, no, they can be diferent sizes – Alexandre Bento Oct 08 '19 at 13:27
  • How many images are we talking about? Would it be manageable to manually split the images and save the results as 3 individual images? Then you could do as @Dom says. Or does this have to happen dynamically? – Rob Moll Oct 08 '19 at 13:32
  • the ideal is to be dynamically. I open the map and fix the page size of 25 for example, if map size is 100, they divide it into 4 pieces, put them on column to fit the page to print – Alexandre Bento Oct 08 '19 at 13:38
  • Sounds complicated, you might be better off enforcing print on landscape https://stackoverflow.com/questions/138422/landscape-printing-from-html – Dominic Oct 08 '19 at 13:47
  • I edited it with some real images, to make it easier to understand – Alexandre Bento Oct 08 '19 at 13:51

1 Answers1

3

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>
Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181