I have a big table with lots of columns that I need to fit into a PDF page using DOMPDF. Just like here, my headers are much wider than the corresponding content, so I'm trying to rotate them. The HTML opened in Firefox seems ok, but the resulting PDF is not.
The Setup
HTML:
<td class="cell rotated_vertical_td" style="width:3%;">
<div class="rotated_vertical">Rotated_</div>
</td>
The width attribute above is being calculated in PHP based on the total number of columns.
CSS:
.cell {
font-size: 8pt;
}
.rotated_vertical_td {
height: 280px;
width: 20px;
text-transform:uppercase;
margin:0;
padding:0;
}
.rotated_vertical {
-webkit-transform:rotate(270deg);
-moz-transform:rotate(270deg);
-ms-transform:rotate(270deg);
-o-transform:rotate(270deg);
transform:rotate(270deg);
transform-origin: 50%;
width: 20px;
}
I had to apply the .cell class to each <td> because DOMPDF was not picking up the table td rule for some reason.
The Problem
It seems like DOMPDF first renders the text, changes the table cell accordingly, and then rotates it. Which means that the column still takes as much space and that breaks the whole point.
I've tried using substr() to cut the text to be only 2, 4 or 8 characters long. Looks like the column widths are adjusting accordingly.
Those are the screenshots of the actual PDF being rendered. As you can see, the last one fits less columns, even though the markup and the CSS is the same. Only thing thats changed is the characters count. Looks like it completely ignores the width I set on those headers.
In case of HTML, it looks like that width: 20px; on the inner div makes a difference - if I remove it, the HTML headers become wide as well.
So again, it looks like the inner div width stretches the table header cells. I can override that width and it works for HTML, but it does not for PDF.
What makes it worse is that DOMPDF does not seem to support having multiple page orientations in a single document so I can't have that page in the landscape mode.
In an answer to this question it is advised to use the absolute positioning, but I am not sure how that'd work with DOMPDF. For example, they treat the position:fixed elements as page headers, might have something reserved for the absolute positioned ones.
Please help
Update 2015-01-26
Thanks to BrianS for his help, I've managed to make the text rotate using the CSS approach he suggested and the latest DOMPDF downloaded from GitHub. Before that, I was generating the dynamic images with rotated text for each one of the headers.
Several things I'd like to point out for those who got here searching for the solution (including the future me I guess)
Positioning the content is a nightmare. There seems to be no way to predictably control the position and behaviour of the rotated headers neither in Firefox, nor in DOMPDF, and that is frustrating. First I had to change the transform-origin property to be:
transform-origin: left bottom 0;
... just so it's position is less random because otherwise changing either top/left or width/height properties kept moving the block in both dimensions. The fact that there are about 7 variables to control (top,left,width/height of the :after element, as well as the line-height and width/height of the wrapping cells) makes it impossible to go through all possible combinations of those to get the desirable position.
If a header is too long and contains any breakable characters - such as spaces - it gets split into several lines and those lines get combined into one by overlaying each other. Please see:
I couldn't find a way to prevent that. Seeing that the text that doesn't contain any breakable characters is still being placed on one line,
tried playing with the width/height/line-height controls
tried replacing the spaces with inside of the css content property but that gets printed directly and does not act as the non-breakable space
tried using the ASCII characters as advised here but it outputs some weird characters instead
replaced the spaces with underscores - it works but looks ugly
so I've tried replacing the spaces with <span style="color:#fff">_</span> so this way the color of the underscore matches the background, but again, that whole thing gets printed directly, and I've also realised that that span would break the line anyway
it feels like that line that gets combined just doesn't have enough width, but if I change the width of the .rotate .content:after element, it just moves that overlayed line to the right, although when I open the HTML in Firefox that seems to help
So for now, my solution is using the underscores, but that doesn't look professional. I'd appreciate if you could help me out with a soution.
Here's the updated setup:
HTML:
<tr class="table_summary_thead">
<td class="rotated_vertical_td" style="width:3%;">
<a hred="appendix_item_44">
<div class="rotated_vertical_outer">
<div class="rotated_vertical_inner rtb1f73b0b57649457abd0ca2e0e8c94e0f7d79c25">
</div>
</div>
</a>
<style type="text/css">
.rtb1f73b0b57649457abd0ca2e0e8c94e0f7d79c25:after {
content:"COMPREHENSIVE";
}
</style>
</td>
</tr>
CSS:
.table_summary {
width:100% !important;
}
.table_summary td {
text-align:center;
}
.table_summary tbody td {
font-size: 10pt;
padding: 4px;
}
.rotated_vertical_td {
width: 20px !important;
height: 680px !important;
font-size: 8pt;
margin:0;
padding:0;
text-align:left;
}
.table_summary_thead {
line-height: 220px;
text-align:left;
}
.rotated_vertical_outer {
position: relative;
overflow: visible;
text-align:left;
}
.rotated_vertical_inner:after {
height: 150px;
overflow: visible;
position: absolute;
text-align: left;
transform: rotate(270deg);
transform-origin: left bottom 0;
width: 20px;
top: 100px;
left: 110px;
}
Including most of the CSS here because who knows, some small part that seems irrelevant might make a difference.
There's a lot to cover in this question. In the future you might want to focus a bit more. Let's get started.
First, your impression of how dompdf deals with tables is correct. Cells are rendered before transforms are applied. Actually, I'm pretty sure right now that transforms have no effect on the flow of the document, just the appearance of the transformed content.
Second, it is true that dompdf does not currently support multiple page orientations. If you want to use dompdf for that you'll have to create each PDF separately and use something like pdftk or fpdf/fpdi to combine the results.
Third, position: fixed isn't treated as page headers but as the CSS spec outlines, i.e. persistent across pages. position: absolute is also treated per the spec (for the most part). Absolutely positioned content is placed at the specified coordinates according to either a) the page where it is encountered or b) the first parent element without static positioning.
If you set a parent element to position: relative and then absolutely position one of it's children the child element will be positioned relative to the parent. This is why the last referenced question recommends that styling. It should work in dompdf, except that dompdf's wonky table handling is resulting in the wrong column width.
So how to work around the issue? If you leave the content out of the HTML then dompdf will render it the width you specify. You can add the content back in using CSS. The following seems to work well enough:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<style type='text/css'>
td {
width: 20px;
}
.rotate .container {
position: relative;
overflow: visible;
}
.rotate .content:after {
width: 150px; height: 150px;
overflow: visible;
content: "Overall Satisfaction";
transform: rotate(-90deg);
transform-origin: center center;
position: absolute; left: -150px; top: -200px;
}
</style>
</head>
<body>
<table border="1">
<thead>
<tr style="line-height: 200px;">
<th><div>Facility</div></th>
<th><div>Date</div></th>
<th><div>Score</div></th>
<th class="rotate"><div class="container"><div class="content"></div></div></th>
</tr>
</thead>
<tbody>
<tr>
<td>Los Angeles</td>
<td>11/12/2010</td>
<td>3.5</td>
<td>2.5</td>
</tr>
<tr>
<td>San Diego</td>
<td>11/17/2010</td>
<td>10.0</td>
<td>10.0</td>
</tr>
</tbody>
</table>
</body>
</html>
Make sure you're using dompdf 0.6.1.
I had the same problem and tried out the answer of BrianS, which worked perfectly - for the first page.
However, when the table spanned several pages (with the header being repeated), the rotated texts on all following pages were positioned wrongly.
Thinking a bit more about this, I found a surprisingly simple solution IF, LIKE ME, YOU WANT THE ROTATED TEXTS IN EXACTLY THE SAME POSITION ON ALL PAGES (if not, then this will not work): Just don't draw the texts at all during the PDF creation, but instead add them later on like the page numbering.
That is, simply create the cells with the needed height and width but leave them empty in the actual HTML layout.
Then add the texts afterwards like this:
// assuming you loaded the HTML,
// create the PDF and grab the canvas
$dompdf->render();
$canvas = $dompdf->get_canvas();
// add rotated text!
$font = Font_Metrics::get_font("helvetica");
$canvas->page_text(250, 50, "Rotated", $font, 9, array(0,0,0), 0, 0, 270);
$canvas->page_text(300, 50, "Text", $font, 9, array(0,0,0), 0, 0, 270);
The last parameter here is the angle of rotation. Attention: There seem to be different dompdf versions out there, with some of them having two spacing parameters between the color and the rotation (like shown here), and some of them having one spacing parameter between the color and the rotation.
First, set height of A_LONG_HEADER height:125px and set left and right margin in inside div margin-left: -50px margin-right: -50px. The following seems to work well:
HTML
<table id="codexpl">
<tr >
<th >#</th>
<th><span >98</span></th>
<th id="rotate"><div id="vertical">A_LONG_HEADER</div></th>
</tr>
<tr>
<td>1</td>
<td>This</td>
<td>c</td>
</tr>
<tr>
<td>2</td>
<td>6</td>
<td>two</td>
</tr>
<tr>
<td>3</td>
<td>is</td>
<td>not</td>
</tr>
<tr>
<td>4</td>
<td>the</td>
<td>Column</td>
</tr>
<tr>
<td>5</td>
<td>first</td>
<td>One</td>
</tr>
</table>
<p>TEST</p>
CSS
#rotate
{
height:125px;
}
#vertical
{
-webkit-transform:rotate(-90deg);
-moz-transform:rotate(-90deg);
-o-transform: rotate(-90deg);
margin-left: -50px;
margin-right: -50px;
}
Insert info in different places per page can be implemented by inline PHP:
$header = array();
foreach($participants_t as $p_id => $p) {
$header[] = $p['Title'];
}
...
$dompdf = new DOMPDF();
$html = htmlspecialchars_decode(htmlentities($html, ENT_NOQUOTES, 'UTF-8'), ENT_NOQUOTES);
$dompdf->load_html($html);
$dompdf->set_paper("A4", "landscape");
$dompdf->render();
$pdf = $dompdf->get_canvas();
$GLOBALS["header"] = $header;
if (isset($pdf)) {
$pdf->page_script(
'$plus_top = 70; '
. 'if ($PAGE_NUM > 1) { '
. '$plus_top = 0; '
. '} '
. '$font = Font_Metrics::get_font("DeJavu Sans"); '
. '$header = array(); '
. '$header = $GLOBALS["header"]; '
. 'foreach($header as $key => $item) { '
. '$pdf->text(122 + 22.01 * $key, 178 + $plus_top, $item, $font, 9, array(0,0,0), 0, 0, -90); '
. '} '
. '');
}
return $dompdf;
So at first page ($PAGE_NUM = 1) top position is 70px bigger.
This method has one problem - font subset isn't created for text inserted by inline PHP.
I've got a pretty regular HTML <table> with one cell that spans multiple rows via rowspan. Inside of this cell I've got a <div> that I want to occupy the entire height of the cell but for the life of me I can't seem to figure it out. It seems similar to this post which mentions this Chrome bug but also seems so simple that maybe I'm just not thinking clearly.
Here's a stripped down version of my HTML:
<table>
<tr>
<td class="a" rowspan="2"><div>A</div></td>
<td class="b"><div>B</div></td>
</tr>
<tr>
<td class="c"><div>C</div></td>
</tr>
</table>
And CSS:
td
{
vertical-align: top;
}
td.a div
{
background-color: #f00;
height: 100%;
}
And a JSFiddle. And here's what I'm getting and what I'm trying to get:
What's really weird is if I use Chrome's inspector to change the <div> to display: inline-block and then set it back to display: block it actually looks pretty much exactly how I want it to.
(And no, switching away from a table isn't an option for this project, there's other code not shown that requires that.)
Option 1
Simply add overflow:auto; to your div CSS
Demo Fiddle
td
{
vertical-align: top;
}
td.a div
{
background-color: #f00;
height: 100%;overflow:auto;
}
Option 2
Alternatively you'll need to define the height of your table in order for the child to be able to calculate what its 100% is 100% of.
Option 3
The only other way would be to set position:relative on the td elements then position:absolute for the child div
Dear css experts: In my field, it is common to denote statistical significance with *'s on the numbers. I know I could put all stars in their own td field and then use css so that numbers are aligned, but I was wondering if this can be done in native css. the intent is to have an ability to realign numbers:
<html>
<head> <style type="text/css"> td { text-align:right; } </style> </head>
<body>
<table>
<tr> <th>c</th> </tr>
<tr> <td>1</td> </tr>
<tr> <td>11</td> </tr>
<tr> <td>111</td> </tr>
<tr> <td>1111</td> </tr>
<tr> <td>11<sup style="text-width:0pt">**</sup></td> </tr>
</table>
</body>
possible?
Yes, it’s possible, and it’s a novel idea (at least new to me). The idea is apparently to have a column with numbers, aligned to their last digit, but with one or more characters (here, asterisks) to the right of some numbers. Wrapping those characters to an element, setting the element’s width to zero, and letting the content overflow (overflow: visible, the default) does the job. However, the width needs to be set using the width property, which has no effect on inline (text-level) elements, and sup is inline by default, so this needs to be fixed by making it an inline block (which is affected by the width property):
<style>
td { text-align:right; }
.after { display: inline-block; width: 0; }
</style>
...
<tr> <td>11<sup class=after>**</sup></td> </tr>
I have used the same markup as in the question. However, the sup element has several technical problems (e.g., it may cause line misalignment), so I would use span instead. You can style it in superscript style if desired. But e.g. Chicago Manual of Style, clause 3.78, uses simple asterisk “*” characters in text in this context, with no attempt at raised position or reduced font size. The point is that in any many commonly used fonts, the asterisk itself is, by typographic design, superscript-like: above the baseline and relatively small.
Here’s a screenshot of a test that uses this technique, first with sup, then, on the last row, with span. The font is Cambria.
If I understand you correctly, you want to add '*' via css. You can do this:
td:after {
content: '*';
font-size: .8em;
vertical-align: top;
}
DEMO
I have a external css for reset that apply to all table, td, div, etc..
In my website, there is a customer template that allow user to create their desire content in html.
When view this customer template in webpage, I don't want to apply above external css.
So I put this customer template content into a div, and trying to exclude this div and all childs of it from css.
Is it possible to write css selector to select all table, td, div, etc... which are not child of given div id?
Below is my testing html code and reset.css
The content of div customtemplate is dynamic enter by user.
User try border=1 to table, it apply all table cell to border 1 without reset.css.
But with reset css, there is no border appears.
My users are not html/css professional, so they will create simple html and expect to display as they write in their test page.
If their html not appear exactly the same for all browsers, it is up to them.
I have no responsible for it. I just need to display their html in div without reset.css.
Anyway to solve ?
<html>
<head>
<link href="reset.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<div class="customtemplate">
<table border="1" cellpadding="10px">
<tr>
<td>abcd</td><td>def</td>
</tr>
<tr>
<td>hello</td><td>world</td>
</tr>
</table>
</div>
<br><br>
<table>
<tr>
<td>default</td><td>reset</td>
</tr>
<tr>
<td>style</td><td>using</td>
</tr>
</table>
</body>
</html>
table, td {
border: 0;
padding: 0;
}
The right way would be to define YOUR defaults for that div. The reset CSS helps you avoid the problems that are caused by different default values in browsers. So for that div with the given ID, you should define some sensible defaults, which are different from the defaults used on the site, but still can be expected to be the same default on every browser.
For example:
/* reset.css */
p { margin: 0; }
/* yourdefaults.css */
#yourdiv p { margin-bottom: 10px; }
As it turns out I don't know CSS.
I ran into a brick wall after using Eric Meyer's CSS reset (http://meyerweb.com/eric/tools/css/reset/)
I have a table with this style
table.home_right_top, .home_right_top table, .home_right_top
{
background-color: #F2F2F2;
width: 100%;
padding: 10px 20px 15px 20px;
}
but the padding is not applied to the table at all and I cannot figure out why. I am happy that I see the same behavior on all the browsers including IE7 and IE8 but I don't see any padding. Can someone please tell me what I am doing wrong here?
Thanks.
EDIT
This is my table
<table class="home_right_top" border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="blueHeading14 heading_padding_right" style="width: 64px">Products</td>
<td class="rpt_stroke" style="width: 280px"> </td>
</tr>
</tbody>
</table>
The problem isn't the reset, it's that the W3 CSS property spec states that padding can be applied to:
all elements except table-row-group,
table-header-group,
table-footer-group, table-row,
table-column-group and table-column
So it's invalid to apply padding to a <table>. Instead, the only solution that comes to mind is to apply margin instead, wrap the table in a <div>, or apply the padding to the individual <td>s with special classes.
Take a look at the last line in his css:
table {
border-collapse: collapse;
border-spacing: 0;
}
Try removing that and seeing what happens, table cells don't often act like block level elements. I think the real problem here is that you shouldn't style the table element like this, becasue it's display property by default is table which is not the same as the box model.
Try putting padding on the cells themselves or add a margin to the table.
Works fine for me. Did you declare a DocType?
You have to apply the style to the TD's not the table.
table.home_right_top td