19

I am working on small web based application where user is presented 2-3 page long report which can be printed as PDF. I looked at different solutions on stackoverflow / internet and found somewhat working solution to printing side (contents are printed with extra margins but i need to work on that to fix it) my current problem is i am not able to display html content in browser with page like layout. I am able to show 1st page with A4 size but as soon as content goes beyond 1 page it appears as if it printed outside page, you can check the images below

  1. How page is shown in Browser enter image description here

  2. How it's print preview look like enter image description here

Here is the CSS

.A4 {
   background: white;
   width: 21cm;
   height: 29.7cm;
   display: block;
   margin: 0 auto;
   padding: 10px 25px;
   margin-bottom: 0.5cm;
   box-shadow: 0 0 0.5cm rgba(0,0,0,0.5);
}

@media print {
.page-break { display: block; page-break-before: always; }
size: A4 portrait;
} 

@media print {
 .noprint {display:none;}
 .enable-print { display: block; }
}

I am trying to solve below problems,

  1. Would love if all the report is shown with page like layout (additionally, if i can show pages in horizontal pagination instead of long vertical page)
  2. No padding issues while printing, what you see is printed!
Community
  • 1
  • 1
Ninad Mhatre
  • 549
  • 1
  • 7
  • 17
  • the extra padding appears because the browser prints it right from the and not from .A4 .. you might want to disale the padding of the .A4's container before you print, to print what you see – Jones Joseph Sep 14 '16 at 09:04
  • Your .A4 is actually bigger than 21x29.7 because of the padding. You need to add `box-sizing: border-box;` to make the padding go "inward" from the stated size. Also, you need to manually separate the content into pages somehow. One way this might work is by using columns. –  Sep 14 '16 at 09:23

3 Answers3

17

Your 2nd problem:

You have to set the body margin and padding to zero. You also need to remove box shadow, margin, width and height from the A4 class in order to print multiple pages.

.A4 {
  background: white;
  width: 21cm;
  height: 29.7cm;
  display: block;
  margin: 0 auto;
  padding: 10px 25px;
  margin-bottom: 0.5cm;
  box-shadow: 0 0 0.5cm rgba(0, 0, 0, 0.5);
  overflow-y: scroll;
  box-sizing: border-box;
}

@media print {
  .page-break {
    display: block;
    page-break-before: always;
  }

  size: A4 portrait;
}

@media print {
  body {
    margin: 0;
    padding: 0;
  }

  .A4 {
    box-shadow: none;
    margin: 0;
    width: auto;
    height: auto;
  }

  .noprint {
    display: none;
  }

  .enable-print {
    display: block;
  }
}

Your first problem:

You could try to create a pagination feature by calculating the scrollheight, and keep removing elements from the pages untill the scollheight is smaller than the page itself.

Example: https://jsfiddle.net/tk8rwnav/31/

var max_pages = 100;
var page_count = 0;

function snipMe() {
  page_count++;
  if (page_count > max_pages) {
    return;
  }
  var long = $(this)[0].scrollHeight - Math.ceil($(this).innerHeight());
  var children = $(this).children().toArray();
  var removed = [];
  while (long > 0 && children.length > 0) {
    var child = children.pop();
    $(child).detach();
    removed.unshift(child);
    long = $(this)[0].scrollHeight - Math.ceil($(this).innerHeight());
  }
  if (removed.length > 0) {
    var a4 = $('<div class="A4"></div>');
    a4.append(removed);
    $(this).after(a4);
    snipMe.call(a4[0]);
  }
}

$(document).ready(function() {
  $('.A4').each(function() {
    snipMe.call(this);
  });
});

This example breaks on every element. The paragraphs don't break on words, but you can implement this, but that will get complicated very fast.

Simon Backx
  • 1,282
  • 14
  • 16
  • thanks a lot for the help! specially for jsfiddle link :) ... i will try it today and see how it works – Ninad Mhatre Sep 15 '16 at 09:11
  • @SimonBackx Can you please look into my question. It is the same exception but only with TS and Angular framework. https://stackoverflow.com/questions/64133505/how-to-split-html-page-in-a4-size-in-angular-9 – TheCoderGuy Sep 30 '20 at 12:14
  • @Simon Backx it works, but if the last paragraph of first page has a lot of text then it moved to the next page, so there's a lot of space remain. Is there any solution? – frozenade Mar 02 '22 at 01:26
2

Below is a revised version of the snipMe() function to ensure elements in Page 2-n are in the original order. I also added comments.

   function snipMe() {
      page_count++;
      if (page_count > max_pages) {
        return;
      }
      var long = $(this)[0].scrollHeight - Math.ceil($(this).innerHeight());
      var children = $(this).children().toArray(); // Save elements in this page to children[] array
      var removed = [];
      // Loop while this page is longer than an A4 page
      while (long > 0 && children.length > 0) {
        var child = children.pop(); // Remove last array element from the children[] array 
        $(child).detach();  // JQuery Method detach() removes the "child" element from DOM for the current page
        removed.push(child);  // Add element that was removed to the end of "removed" array
        long = $(this)[0].scrollHeight - Math.ceil($(this).innerHeight()); // Compute current size of the page 
      }
      // If elements were removed from the page 
      if (removed.length > 0) {
        var rev_removed = removed.reverse();  // Reverse the order of the removed array
        var a4 = $('<div class="A4"></div>'); // Start a new page
        a4.append(rev_removed); // Add elements removed from last page to the new page
        $(this).after(a4); // Add the new page to the document
        snipMe.call(a4[0]); // Recursively call myself to adjust the remainder of the pages
      }
    }
Mike Meinz
  • 506
  • 3
  • 9
  • Thanks, I've updated my original answer to fix this bug. I've replaced `push` with `unshift`. That's a cleaner approach than using the reverse function. – Simon Backx Mar 02 '17 at 15:09
  • Adding to the beginning of an array is expensive since all elements have to be shifted upon each .unshift() call. I ran the JavaScript in the following comment in JSFiddle and got the these results showing .push() and .reverse() are far more efficient. Using .unshift() 778ms Using .push() and .reverse() 18ms – Mike Meinz Mar 04 '17 at 16:34
  • `var removed=[]; var timeDiff; var count = 0; var startTime = new Date(); while (count<100000) { removed.unshift(count.toString()); count=count + 1; } timeDiff = (new Date()) - startTime; document.write("Using .unshift() " + timeDiff.toString() + "ms"); removed=[]; count = 0; var removed1=[]; startTime = new Date(); while (count<100000) { removed1.push(count.toString()); count = count + 1; } var rev_removed=removed1.reverse(); timeDiff = (new Date()) - startTime; document.write("
    Using .push() and .reverse() " + timeDiff.toString() + "ms"); `
    – Mike Meinz Mar 04 '17 at 16:35
  • I ran your script and had "Using .unshift() 18ms; Using .push() and .reverse() 16ms". Not a huge difference, but I get your concerns. My script is just an example, not production ready ;) – Simon Backx Mar 06 '17 at 15:36
  • Strange that you saw 18ms for .unshift(). I pasted the above JavaScript into JSFiddle this morning again and saw: Using .unshift() 761ms Using .push() and .reverse() 22ms – Mike Meinz Mar 06 '17 at 15:39
  • The implementation and time complexity of unshift, reverse and push is not something defined by standards, so that is very browser dependent ;) – Simon Backx Mar 06 '17 at 15:40
  • I saw similar results from Chrome, Internet Explorer and Edge browsers. What browser did you use to get such a low elapsed time for the .unshift() test? – Mike Meinz Mar 06 '17 at 15:56
  • I used Safari (macOS) ;) – Simon Backx Mar 06 '17 at 18:02
0

By default a margin is added for printing aswell. If you click on "More settings" there is a dropdown menu for Margins. select None to remove all margins.

That way you are able to handle the margins within css.

Niels Peeren
  • 315
  • 1
  • 13