25

I want to print a large table (so large that its rows are approx. 3 sheets of papers wide) from HTML. If possible, CSS should suffice for layout and the solution should work with different browsers.

I'm currently defining the following style rules:

table { page-break-inside:auto; }
tr    { page-break-inside:auto; }

When I inspect the DOM elements e.g. in Firefox 33.0.2 (on OS X) I can see that the rules are recognized, but then when I look at a print preview ( File | Print | PDF | Open PDF in Preview) all columns that don't fit on the first page are cut off, i.e. I receive 1 page of printed output instead of 3. I have also tried Internet Explorer 11 and 10 to the same effect.

So how can I layout large HTML tables (ultimately large both in terms of columns an rows) for print out using CSS?

Bonus question: If page-break style components indeed only apply to block-level elements as is indicated in this previous answer, would it help if I construct my table from divs instead of tds when aiming for print output?

UPDATE

Here is a relevant sample that I just tried on JSFiddle. (I don't have an account there, so FWIK I cannot provide a direct link.)

HTML:

<body>
<table>
<tr>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_0</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_1</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_2</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_3</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_4</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_5</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_6</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_7</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_8</td>
<td>The_quick_brown_fox_jumped_over_the_lazy_dog_A_9</td>
</tr>
</table>
</body>

CSS:

table { page-break-inside:auto; }
td    { border:1px solid lightgray; }
tr    { page-break-inside:auto; }

If I try to print this table (e.g. by applying This Frame | Print Frame ... | PDF | Open PDF in Preview to JSFiddle's Result view in Firefox 33.1 for OS X and for Paper Size/Orientation A4/Portrait) I get one page of output. All columns but the first and part of the second are cut off.

Community
  • 1
  • 1
Drux
  • 11,992
  • 13
  • 66
  • 116

3 Answers3

32

You absolutely need to move away from a table if you need readable vertical printing on the page. Tables are great for display on the page when it's tabular data but are not a viable solution for printing as they don't respect flow.

There are plugins (like this one here, no affiliation – just a Google result) that will do this automatically for you, but here's the example. When you use this, make sure that the @media print is listed appropriately. To test locally, you can change that to @media screen.

That won't show the @page rules listed, but those are visible via a print preview.

Hope this helps:

Fiddle for printing in portrait

HTML

<section class="table">
  <div class="row">
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_0</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_1</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_2</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_3</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_4</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_5</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_6</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_7</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_8</div>
    <div>The_quick_brown_fox_jumped_over_the_lazy_dog_A_9</div>
  </div>
</section>

CSS

@media print {
    @page {
      margin: 2.5cm;   
    }
    div.row > div {
      display: inline-block;  
      border: solid 1px #ccc;
      margin: 0.2cm;
    }
    div.row {
      display: block;
    }
}


.table {
    display: table;
    border-spacing: 2px;
}
.row {
    display: table-row;
}
.row > div {
    display: table-cell;
    border: solid 1px #ccc;
    padding: 2px;
}

Edit - Printing horizontally across several pages:

Okay, so this is probably a far less common use case, and we have to do some goofy things with it – so fair warning. I'll try to explain this step-by-step as it's cryptic and obnoxious.

Fiddle for printing in landscape here!

CSS

@media print {
    @page {
      margin: 0;
    }
    body {
        height: 100%;
        width: 100%;
    }
    div.row > div {
      display: inline-block;  
      border: solid 1px #ccc;
      margin: 0.1cm;
      font-size: 1rem;
    }
    div.row {
      display: block;
      margin: solid 2px black;
      margin: 0.2cm 1cm;
      font-size: 0;
      white-space: nowrap;
    }
    .table {
        transform: translate(8.5in, -100%) rotate(90deg);
        transform-origin: bottom left;
        display: block;
    }
}

This is the part that matters, as it's setting your print directives. Most of this is stuff we've seen in the original (with some tweaks as I was playing with it).

The part we care about comes here:

 .table {
     transform: translate(8.5in, -100%) rotate(90deg);
     transform-origin: bottom left;
     display: block;
 }

What we're doing is flopping the whole thing on its side, and then sliding it to where we expect it to be. translate(8.5in, -100%) is telling the browser – Slide this element 8.5 inches (the width of a standard letter paper in the US) to the right, and then slide it up 100% of its height (the negative indicates up as opposed to down). We slide it to the right 8.5 inches so that it'll appear at the top of the page when rotated. We slide it up its calculated height so that we don't have an ugly gap to the left of the table when the rotation happens either.

Then, we instruct it that we want all of those calculations run in relation to the bottom left of the element's normal position in document flow. This keeps this crazy long table from being rotated way off to the right by setting the left property. The bottom property is important because we're rotating it clockwise a quarter turn, and if we did that from the top, it would be off the page to the left. That quarter turn is described in the next part of the transform statement: rotate(90deg);

Voila. The thing prints across multiple pages.

Before you ask: No. There is no way to prevent the page break inside the element of which I'm aware. I know it's obnoxious, ugly and all that garbage, but we can only work with the tools which we're given.

Update Firefox confirmed working: Firefox 33.1 on Mac

Josh Burgess
  • 9,327
  • 33
  • 46
  • 1
    +1 Could you pls modify this so it gives a horizontal table whose entire width prints across several pages. This is what I originally intended. (This is the most promising answer so far.) – Drux Nov 13 '14 at 06:37
  • 1
    Wait, why use div layout for tabular data when it is possible to `@media print {table, tbody, tr, th, td { display: block; }}`. +1 by the way. – Salman A Nov 14 '14 at 17:25
  • @SalmanA - That works as well. I hadn't even considered that. Good point, +1. Example here: http://fiddle.jshell.net/q5phvm68/12/show/ – Josh Burgess Nov 14 '14 at 17:30
  • @SalmanA - 2 fiddles and a link to a plugin. I labeled the fiddles appropriately. – Josh Burgess Nov 14 '14 at 17:33
  • Updated version does not seem to work with Firefox 33.1. (Gives empty page when printing from JSFiddle.) – Drux Nov 17 '14 at 07:06
  • @Drux – I'm not seeing that on my Firefox 33.1 – Josh Burgess Nov 17 '14 at 14:56
  • Sadly, This does not work when using multiple columns, having different heights - then the cells end up with different heights when printing (but not on screen). See also https://stackoverflow.com/q/47478539/43615 (which is related, using a plain old table). I suspect "display:block" is causing this problem. – Thomas Tempelmann Nov 26 '17 at 11:16
  • @ThomasTempelmann You're right. The part with rotating it still applies to tables just fine though. The `break-inside` rules may not however. – Josh Burgess Nov 26 '17 at 14:44
  • @JoshBurgess I think I found a solution: Adding explicit widths that are higher than the possible width fix the issue in my case. See my answer to my linked question. Does not work with your solution, though. – Thomas Tempelmann Nov 26 '17 at 17:08
  • Yeah, that's one way of going about it. Sorry this solution didn't work for you – Josh Burgess Nov 26 '17 at 17:09
  • Does it work for many rows and many columns at the same time? I tested it and I couldn't get it – joseglego May 27 '21 at 15:53
  • @joseglego as long as you can define heights and widths it should. If not, drop the `display:block;` in favor of keeping it as a table display. – Josh Burgess Jun 01 '21 at 16:13
9

page-break-inside: auto; is the default style, so unless you have a rule setting them differently, those rules will do nothing.

If your table isn't being broken onto multiple pages, then it's most likely because there some other rule you added, which prevents it - my guess an overflow: hidden.

What you first should do is remove all styles, and see if that prints on multiple pages. If yes, start adding your styles back (rule by rule, or if necessary line by line) to find the problem.

RoToRa
  • 37,635
  • 12
  • 69
  • 105
2

If you want to force page break you should use page-break-before or page-break-after. You need to know that the styled element should contain a non-empty block-level element and that it can't be absolutely positioned. However it is quite inconsistent on tables elements. Which leads us to your last question : yes, it would be more consistent to build your tables with divs.

However, considering what you want to achieve (printing large horizontal tables) you should know that trying to fit 3 pages into 1 can't work as your content won't be readable. As a matter of fact, the only best practice would be to use a vertical layout instead (just for print or for both web and print).

Pipo
  • 978
  • 6
  • 19
  • +1 Do you have a reference for a writeup that makes a convincing argument for the alternative use of a vertical layout in such situations. This would perhaps convince a client to change current requirements. – Drux Nov 12 '14 at 07:29
  • @Drux Sure. Can you provide a bit more context for the table so that I can give you relevant examples? Like what is the purpose of the table? – Pipo Nov 13 '14 at 10:26
  • The columns correspond to a period of time. The rows correspond to objects with associated events that are scheduled at particular days during that period. The cells contain event ids. The event ids are long enough such that for extended periods (e.g. 2 months) the table may be wider than a single print page. It would be good to print it (perhaps with title column for object ids repeated on each horizontal print page) regardless. – Drux Nov 13 '14 at 15:24