Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to put a blank space atop of each page of a printed HTML

I've designed an HTML report with several divs and tables. Now my client asks me to put a repeating header at the top of each printed page. This is not possible using plain CSS and HTML as far as I know. Just as a try, I put header div element inside a thead to which I applied display: table-header-group in order to be displayed, and put all other elements of the report as rows of the main table but no success.

  1. A workaround is to use @print { .header {position: fixed; top: 10px} } in order to repeat .header element at the top of each page. But for this work, we should put a blank space at the top of each new page; otherwise fixed header element is mixed with other elements at the top of table.

  2. As another workaround I can compute elements height at render time and put manual page breaks where needed. So I want to know if there is any Javascript library available to execute at page load and computes all render-time heights of div elements of the page and put a zero-height div element with page-break-before: always; before each div which exceeds height of an A4 paper. For suppose the following divs result in 14, 10, 8, 9, 6, 13, and 6 centimeter height at render time. I want the library put a page-break dummy dive element at specified locations:

<div id="d1">...</div>

<div id="d2">...</div>

<!-- here, because 14+10+8 exceeds 30cm -->

<div id="d3">...</div>

<div id="d4">...</div>

<div id="d5">...</div>

<!-- here, because 8+9+6+13 exceeds 30cm -->

<div id="d6">...</div>

<div id="d7">...</div>

like image 996
Mohsen Avatar asked Jul 20 '11 13:07

Mohsen


2 Answers

Have you considered splitting the table over multiple pages? I've done this in the past by measuring how large a page would be and then writing out the rows to the browser. Each time the end of a page was reached, I'd close the table being written, then start the next one, writing a new DIV etc. You need to keep a track of the max size of each column etc. as you go, but efficiency wise we could write thousands of rows to the screen and would still typically return in well under 60 seconds for big reports.

I also used a CSS orientation feature only available in IE at the time (writing-mode:tb-rl) to mimic the page being rendered in Landscape and the content likewise - it does require a little more thought in how you're writing it, BUT the resulting content looked very professional.

like image 162
burgen Avatar answered Oct 18 '22 09:10

burgen


Here is my final solution. The following code is executed at page load. My page was solely composed of a number of divs. Two divs as headers and the other divs (containing tabular data) as details. I assumed always printing in portrait scheme and also assumed A4 size (= ~21x30cm). I also assumed 4.5cm margins for left-right and top-bottom of the page. The code first resizes divs to a portrait compatible width (so that all tables are divs are resized automatically). Then I iterate over non-header div elements to add a page break when height exceeds A4 height. I also cloned and added header elements atop of each page through this code.

// page width in pixels
function pw() {
    return cm2px(16.5);
}
// page height in pixels
function ph() {
    return cm2px(25.5);
}
function px2cm(px) {
    return px * 2.54 / 96;
}
function cm2px(cm) {
    return cm * 96 / 2.54;
}

$(function() {
    $('.section > div').each(function(){
        $(this).width(pw() + 'px');
    });
    hh = $('.section > div.heading');
    headingHeight = hh.eq(0).height() + hh.eq(1).height();

    h1 = hh.eq(0).clone();
    h2 = hh.eq(1).clone();

    var h = headingHeight;
    var pageHeight = ph();

    $('.section > div').not('.heading').each(function(){
        if (h + $(this).height() + 14 > pageHeight) {
            h1.css('page-break-before', 'always');
            $(this).before(h1.clone());
            $(this).before(h2.clone());
            h = $(this).height() + 14 + headingHeight;
        } else {
            h += $(this).height() + 14;
        }
    });
});
like image 43
Mohsen Avatar answered Oct 18 '22 09:10

Mohsen