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.
Related
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
Hello I am trying to make an online chat application.
I have the html:
<body>
<table align="center" width="80%">
<tbody class="scroll">
<!--All of the chat-->
</tbody>
<tbody class="formheight" style="width:100%">
<tr>
<td>
<form style="width:100%" action="Write-to.php" method="post">
<input autocomplete="off" name="txt" type="text" id="usermsg" style="font-size:2.4vw;" value="" />
</form>
</td>
</tr>
</tbody>
</table>
</body>
And the css:
html, body
{
height: 100%;
max-height:100%;
overflow: hidden;
}
table
{
height: 100%;
}
tbody {
overflow: auto;
width:100%;
}
thead > tr, tbody{
display:block;
}
I want the 2nd tbody (The one that contains the form) to lie at the bottom of the page and the first to fill the rest of the page upwards.
Currently I am having to use jquery to (kind of) do what I want. Currently the form is half hidden. I would rather do this all with CSS so that it works better with mobile devices.
How could I do that?
Jquery:
var heighty = $(window).height();
var height = $('.formheight').height();
$(".scroll").css("height", heighty - height + "px");
I also can't for the life of me get the form text input to be 100% width?
Please see JSfiddle
I am also very open to another way of laying out this chat app all together!
This is possible in CSS, but would be very difficult to get working across all browsers. Instead, here is my recommendation:
Create an element that fills up 100% height with a bottom padding set to X px.
Create an element with position:fixed and a height of X px.
Give the latter element a z-index:2 and the former a z-index:1. z-index doesn't need to be assigned manually, elements further down in source code automatically have a higher priority and are displayed over previous elements (if they overlay visually).
If you want, you could use a different unit. Percents are very easy because you can have them add up to 100%, so no need for a margin. Of course each has its respective drawbacks, but in my experience what I've described generally has good compatibility and displays comparably on all devices. You could even use CSS #media queries to change the height, X, for different devices.
You need to use something what we call a "Sticky Footer", In your case, your second body goes in the sticky footer. Have a look at this http://ryanfait.com/sticky-footer/ or this http://www.cssstickyfooter.com/ for the css+html for a sticky footer
I have a div that contains a table. On the click of a button in the table, I want to rotate the div and its contents by 90 degrees and hide everything but the header offscreen. Here is an example of this working in IE10:
However, this does not work in IE7 and IE8. Inspecting the elements via the developer toolbar, it seems that the table is not rotating. The div seems to be rotating, because the outline appears where it should be... However, it's like the actual UI isn't updated to reflect this rotation?
Any ideas what might be happening and how to fix this?
Some relevant css for IE7:
#my-div {
width: auto;
float: left;
position: relative;
overflow-y: auto;
overflow-x: hidden;
max-width: 200px;
min-height: 35px;
height: 100%;
display: block;
}
#my-div.hide
{
overflow: hidden;
height: 35px;
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
}
#my-div table
{
width: auto;
}
And the html is nothing special. It's laid out like this:
<div id="my-div">
<table>
<thead>
<tr>
<th>
<button>X</button>
</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
</tr>
...
</tbody>
</table>
</div>
Your code should work for IE7. If you are unable to get it work there is a JavaScript Library called CSS Sandpaper that allows you to use CSS code for rotations and works for older versions of IE.
Edit: I was playing with the example code from the MS Developer Network and noticed that their example works fine on all versions of IE. If I change the position: to relative instead of absolute the rotation stops working in IE7. You might want to test that on your end since you are using relative positioning on your element.
If that does not help you a full sample of your HTML code or a fiddle would be helpful so that the community could better help you.
The issue was that I was using developer tools to switch between browsers. Apparently, this changes some browser security settings. The way this would actually look in IE7/IE8 is not accurately represented by using developer tools in IE10.
I downloaded IETester, and the content is not having this issue anymore. False alarm.
on associated click event of the button use jquery to rotate the div and all its children
function changeOrientation(){
$('#div').attr('webkit-transform', 'rotate(90deg)');
//Loop inside all elements in the div and add the same to all the elemnt
}
javascript code
function changeOrientation(){
document.getElementById('#div').style.webkit-transform = 'rotate(90deg)';
//Loop inside all elements in the div and add the same to all the elemnt
for(var count = 0; count < document.getElementById('#div').children.length; count++){
document.getElementById('#div').children[count].style.webkit-transform = 'rotate(90deg)';
}
}
give a try and let me know if it works.
When I try to use position: relative / position: absolute on a <th> or <td> in Firefox it doesn't seem to work.
Easy and most proper way would be to wrap the contents of the cell in a div and add position:relative to that div.
example:
<td>
<div style="position:relative">
This will be positioned normally
<div style="position:absolute; top:5px; left:5px;">
This will be positioned at 5,5 relative to the cell
</div>
</div>
</td>
That should be no problem. Remember to also set:
display: block;
Since every web browser including Internet Explorer 7, 8 and 9 correctly handle position:relative on a table-display element and only FireFox handles this incorrectly, your best bet is to use a JavaScript shim. You shouldn't have to rearrange your DOM just for one faulty browser. People use JavaScript shims all the time when IE gets something wrong and all the other browsers get it right.
Here is a completely annotated jsfiddle with all the HTML, CSS, and JavaScript explained.
http://jsfiddle.net/mrbinky3000/MfWuV/33/
My jsfiddle example above uses "Responsive Web Design" techniques just to show that it will work with a responsive layout. However, your code doesn't have to be responsive.
Here is the JavaScript below, but it won't make that much sense out of context. Please check out the jsfiddle link above.
$(function() {
// FireFox Shim
// FireFox is the *only* browser that doesn't support position:relative for
// block elements with display set to "table-cell." Use javascript to add
// an inner div to that block and set the width and height via script.
if ($.browser.mozilla) {
// wrap the insides of the "table cell"
$('#test').wrapInner('<div class="ffpad"></div>');
function ffpad() {
var $ffpad = $('.ffpad'),
$parent = $('.ffpad').parent(),
w, h;
// remove any height that we gave ffpad so the browser can adjust size naturally.
$ffpad.height(0);
// Only do stuff if the immediate parent has a display of "table-cell". We do this to
// play nicely with responsive design.
if ($parent.css('display') == 'table-cell') {
// include any padding, border, margin of the parent
h = $parent.outerHeight();
// set the height of our ffpad div
$ffpad.height(h);
}
}
// be nice to fluid / responsive designs
$(window).on('resize', function() {
ffpad();
});
// called only on first page load
ffpad();
}
});
Starting with Firefox 30, you'll be able use position on table components. You can try for yourself with the current nightly build (works as standalone): http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/
Test case (http://jsfiddle.net/acbabis/hpWZk/):
<table>
<tbody>
<tr>
<td style="width: 100px; height: 100px; background-color: red; position: relative">
<div style="width: 10px; height: 10px; background-color: green; position: absolute; top: 10px; right: 10px"></div>
</td>
</tr>
</tbody>
<table>
You can continue to follow the developers' discussion of the changes here (the topic is 13 years old): https://bugzilla.mozilla.org/show_bug.cgi?id=63895
Judging by recent release history, this could be available as soon as May 2014. I can barely contain my excitement!
EDIT (6/10/14): Firefox 30 was released today. Soon, table positioning won't be an issue in major desktop browsers
As of Firefox 3.6.13, position: relative/absolute do not seem to work on table elements. This seems to be long standing Firefox behaviour. See the following: http://csscreator.com/node/31771
The CSS Creator link posts the following W3C reference:
The effect of 'position:relative' on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined. http://www.w3.org/TR/CSS21/visuren.html#positioning-scheme
Try using display:inline-block it worked for me in Firefox 11 giving me positioning capability within the td/th without destroying the layout of the table. That in conjunction with position:relative on a td/th ought to make things work. Just got it working myself.
I had a table-cell element (which was actually a DIV not a TD)
I replaced
display: table-cell;
position: relative;
left: .5em
(which worked in Chrome) with
display: table-cell;
padding-left: .5em
Of course padding usually is added to width in the box model - but tables always seem to have a mind of their own when it comes to absolute widths - so this will work for some cases.
Adding display:block to the parent element got this working in firefox.
I also had to add top:0px; left:0px; to the parent element for Chrome to work.
IE7, IE8, & IE9 are working as well.
<td style="position:relative; top:0px; left:0px; display:block;">
<table>
// A table of information here.
// Next line is the child element I want to overlay on top of this table
<tr><td style="position:absolute; top:5px; left:100px;">
//child element info
</td></tr>
</table>
</td>
The accepted solution kind of works, but not if you add another column with more content in it than in the other one. If you add height:100% to your tr, td & div then it should work.
<tr style="height:100%">
<td style="height:100%">
<div style="position:relative; height:100%">
This will be positioned normally
<div style="position:absolute; top:5px; left:5px;">
This will be positioned at 5,5 relative to the cell
</div>
</div>
</td>
</tr>
The only problem is that this only fixes the column height problem in FF, not in Chrome and IE. So it's a step closer, but not perfect.
I updated a the fiddle from Jan that wasn't working with the accepted answer to show it working.
http://jsfiddle.net/gvcLoz20/
I have the following code that I am using to display a search tool with a scrolling results section. In IE the code works fine:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html style="background:black;height:100%;width:100%;">
<head>
<title>Report</title>
</head>
<body style="background:black;">
<table HEIGHT="100%" WIDTH="100%" style="background:red;">
<tr>
<td>
Search Area
</td>
</tr>
<tr>
<td HEIGHT="100%" WIDTH="100%" style="background:orange;">
<div style="overflow-y:scroll;height:100%;">
<table style="width:100px;height:1000px;">
<tr>
<td style="background:white;">
Results Area
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</body>
</html>
But when I set the meta tag to use IE8 formatting by adding:
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
The bottom DIV tag expands beyond the page. I have tried a number of options though and can't find a way around it without actually specifying a height for the values. Which will not work as I want the page to take up 100% of the screen no matter the size of the browser window.
Any help would be much appreciated.
This metatag enables correct CSS rendering, and in CSS – by design – height:100% basically doesn't work.
You need to give specific height to every single ancestor of the element, including <body>, <table>, <tr> and even <tbody> element that's automatically inserted by the parser.
Anyway, this layout can be achieved in easier way:
.topBanner {
position:absolute; position:fixed;
height:2em;
top:0; left:0; width:100%;
}
body {padding-top: 2em}
this will degrade nicely in IE6, and unlike overflow, will work properly in Mobile Safari.
Edit:
Removing the DOCTYPE declaration will make height="100%" work but it puts the browser in quirks mode though, which is not desirable.
Generally speaking using tables for layout is discouraged, you should use CSS instead.
For example: http://jsfiddle.net/rf649/7/
HTML
<div id="search">Search Area</div>
<div id="results">Results Area</div>
CSS:
#search {
background-color: red;
position: fixed;
height: 150px;
width: 100%;
}
#results{
background-color: orange;
position: fixed;
top: 150px;
width: 100%;
bottom: 0;
overflow: auto;
}
You should set all margins and paddings for the parent elements to zero in order to get what you want.
Update: Sorry, didn't understand the problem at once. Ben's hint should be the better one I assume. :)
Update 2: Oops, since Ben has deleted his answer my first update doesn't make any sense. Try setting the body's height to 100%, that should solve the problem.
My understanding about cross browser CSS is not that big so it might not be the best solution, but it's a solution.
As far as I've seen, you always have to set the height/width of the container that you want to overflow, so you need to set them.
To deal with the resolution I would suggest you to add a jQuery script at the onReady event that dynamically would fix the height and width making the overflow work.
I had the similar problem like you and finally the solution was to modificate a CSS line entry that had an !important modificator for a fixed height declaration. In the HTML code the class (defined in CSS) had the height assigned to 100%, but the CSS applied the !important during the style loading.