PDF Table with slanted header - css

I'm creating PDFs in ColdFusion using cfdocument. I need to make a table with the header row slanted so it all fits on the page. Here's an example of what I'm trying to accomplish. None of the HTML or CSS examples I have found so far have worked. Now I'm wondering if this is a quirk specific to ColdFusion and/or PDFs creation. I know this code came directly from an answer to a similar question here, but it does not create a table with slanted columns in my PDF.
It creates this.
//CSS
* {
box-sixing: border-box;
}
.outerDiv {
background: grey;
height: 200px;
width: 100px;
border: 1px solid black;
border-bottom: 0;
border-left: 0;
transform: skew(-30deg) translateX(58%);
}
th:first-child .outerDiv {
border-left: 1px solid black;
position: relative;
}
.innerDiv {
position: absolute;
width: 250px;
height: 85px;
bottom: -34%;
left: 10px;
transform: skew(30deg) rotate(-60deg);
transform-origin: 0 0;
text-align: left;
}
body,
html {
height: 100%;
}
body {
display: flex;
justify-content: center;
}
table {
border-collapse: collapse;
}
td {
border: 1px solid black;
}
.well {
min-height: 20px;
padding: 5px;
margin-bottom: 10px;
background-color: #f5f5f5;
border: 1px solid black;
border-radius: 3px;
}
.well_tight {
padding: 3px;
margin-bottom: 5px;
background-color: #f5f5f5;
border: 1px solid black;
border-radius: 3px;
}
//ColdFusion/HTML
<cfdocument format="pdf" name="#formname#" pagetype="letter" marginleft=".25" marginright=".25" margintop=".25" marginbottom=".5">
<cfoutput><style type="text/css">#import "/mach15/web/assets/css/formPDF.css";</style></cfoutput>
<div class="well">
<table cellpadding="0" cellspacing="0">
<tr>
<th>
<div class="outerDiv">
<div class="innerDiv">This is first column header</div>
</div>
</th>
<th>
<div class="outerDiv">
<div class="innerDiv">This is second column header</div>
</div>
</th>
<th>
<div class="outerDiv">
<div class="innerDiv">This is third column header</div>
</div>
</th>
</tr>
<tr>
<td> 1 </td>
<td> 2 </td>
<td> 3 </td>
</tr>
<tr>
<td> 4 </td>
<td> 5 </td>
<td> 6 </td>
</tr>
<tr>
<td> 7 </td>
<td> 8 </td>
<td> 9 </td>
</tr>
<tr>
<td> 10 </td>
<td> 11 </td>
<td> 12 </td>
</tr>
</table>
</div>

I work on styling PDF documents using cfdoucment quite a bit and have had a lot of trouble with CSS. so many features aren't supported that would make styling the PDFs so much easier: CSS3 properties, CSS Pseudo elements and not even the !important tag can be used unfortunately.
There are however enough tricks and work arounds that you can use to (somewhat) achieve your desired results, they usually require customizing your markup a bit, Here's how I would go about solving your problem:
First: Getting a table with bordered cells/rows is not a fun task with CSS for CF PDF. One of the CSS Properties that's not supported is border-collapse: collapse; so if you use tables there will be spaces between cells, etc.. you'll end up with something like this for a standard table:
So I would probably generate a separate markup using <div> just for your PDF content and add a pdf-only class or something to it to display only on PDFs and hide elsewhere.
In your questions there are 2 problems that cannot be fixed due to CSS's limitation. 1) slanted lines 2) vertical slanted text.
1) Slanted lines:
I was able to create the slanted blocks by attaching a background image (see below) to the header cells and shifting them along with some other css code that's hopefully easy to follow:
.th {
padding:10px 0;
height: 200px;
position: relative;
left:50px;
border-top: 1px solid #000;
background:url(../img/line.gif) no-repeat right center;
}
Here's the full markup with the CSS:
<div class="table-wrapper pdf-only">
<div class="row th-row">
<div class="th th1"> </div>
<div class="th th2"> </div>
<div class="th th3"> </div>
<div class="th th4"> </div>
</div>
<div class="row td-row first-td-row">
<div class="td td1">Row 1</div>
<div class="td td2"><span class="td-border"></span>r1c1</div>
<div class="td td3"><span class="td-border"></span>r1c2</div>
<div class="td td4"><span class="td-border"></span>r1c3<span class="td-last-border"></span></div>
</div>
<div class="row td-row">
<div class="td td1">Row 2</div>
<div class="td td2">r2c1</div>
<div class="td td3">r2c2</div>
<div class="td td4">r2c3</div>
</div>
</div>
CSS:
.table-wrapper {
width:75%; // so that the last column won't get cut off
position: relative;
}
.row {
width: 100%;
}
.td-row {
border-bottom:1px solid #000;
width: 100%;
position: relative;
}
.td {
padding:15px 0;
text-indent: 10px;
position: relative;
}
.th {
padding:10px 0;
height: 200px;
position: relative;
left:50px; // this should the same as the width of line.gif
border-top: 1px solid #000;
background:url(../img/line.gif) no-repeat right center;
}
/* Adjust the td, th widths below . You can even add different % to
different cols as long as the total adds up to 100% per row. */
.td, .th {
width: 25%;
float: left;
}
.th1 {
border-top: 0;
}
span.td-border {
height:1000px;
width: 1px;
position: absolute;
border-left: 1px solid #000;
left: 0;
top:0;
}
span.td-last-border {
height:1000px;
width: 1px;
position: absolute;
border-left: 1px solid #000;
right: -10px;
top:0;
}
.first-td-row {
border-bottom: 1px solid #000
}
Here's what you'll get: (this is an actual generated PDF using <cfdocument>)
so that takes care of the slanted headers
2) Vertical Text
I can think of 2 solutions, none of which are ideal, but then again we're styling CF PDFs so we'll have to get creative.
To get exactly what you have posted in your question (rotated text) I believe the only way to achieve this is using images instead of texts. Yeah I know it's cheating specially if your tables are going to be dynamic with the header texts constantly changing this won't work. But if this list won't change too much you might as well use images, example:
You would then add another element inside your header cell and set that image as its background and center position it:
<div class="th th1">
<div class="text"></div>
</div>
.th .text {
width:100%;
height:100%;
position:absolute;
}
.th1 .text {
background-image:url(../img/col1-text.gif) no-repeat center center;
}
If using images as texts won't work, you can perhaps loop through the text and and a line break after each letter followed by a space and increment it each time in a descending fashion:
1<br/>
l<br/>
o<br/>
C<br/>
That obviously doesn't rotate the text, but it will make it easier to fit the content in the header.
Hope that helps.

Are you using CF11 or above? If so, please use <cfhtmltopdf> with much better css support instead of the legacy <cfdocument>.
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-g-h/cfhtmltopdf.html

Related

Why does `display: table;` cause a pseudo padding in a div? (And why only when it is itself in a table?)

I have a table.matrix in a div.matrix-wrapper.
The whole thing shall be centered in a bigger div.
I only achieved this by adding display: table; margin: 0 auto; to the wrapper.
(Adding the auto margin to the table is not an option, because of the gray border.)
On its own, the result looks the way I expect. (left)
But when I place it within a table, It looks like the wrapper has a padding. (middle)
When I remove display: table; from the wrapper, the pseudo padding goes away,
but then the centering does not work anymore. (right)
(External links removed.)
Based on the answer given by Alohci, I added this simplified example:
.green {
width: 80px;
height: 50px;
border: 2px solid #0d0;
}
.red {
display: table;
margin: 0 auto;
border: 2px solid red;
}
.blue {
width: 20px;
height: 20px;
margin: 0;
border: 2px solid yellow;
background-color: blue;
}
table {
border: 3px solid black;
border-spacing: 5px;
margin: 20px 0;
}
table td {
border: 3px solid gray;
color: gray;
padding: 5px;
}
<h2>plain boxes</h2>
<p>The red box wraps directly around the blue box with the yellow border.</p>
<div class="green">
<div class="red">
<p class="blue"></p>
</div>
</div>
<h2>boxes in table</h2>
<p>The red box inherits border-spacing from the surrounding table.</p>
<table>
<tr>
<td>
<div class="green">
<div class="red">
<p class="blue"></p>
</div>
</div>
</td>
</tr>
</table>
<h2>plain table</h2>
<table>
<tr>
<td>plain</td>
<td>table</td>
</tr>
</table>
border-spacing and border-collapse inherit. The wrapping table has
border-spacing: 2px;
border-collapse: separate;
applied to it through the user-agent stylesheet, so these values are inherited by your div.matrix-wrapper and have effect when it's given display:table.
To remove the "padding", just set the div.matrix-wrapper to border-spacing: 0px.

How to place text on top of two table cells?

Lets say we have this table:
<table>
<tr>
<td width="50px">Text crossing two td´s</td>
<td width="50px"></td>
</tr>
</table>
How can the text be on top of the two td´s and follow the size of the tr?
https://jsfiddle.net/roj7w1t4/
Is it possible?
EDIT
I need the borders to stay visible. Therefore i cannot use colspan!
Is it possible to create a span and put it over the td´s?
To make more sense what i am trying to do.. this is a small example of my application: What printable element is better to use than linear-gradient?
THE ELEMENT
<div class="elementsDiv ui-draggable ui-draggable-handle" id="29065-1_105" data-weight="938" data-nr="105" style="width: 159.5px; height: 20px; position: absolute; left: 108px; top: 27.1875px;"><table style="height: 100%;"><tbody><tr style="border 1px solid black;"><td style="width: 34.2px; border-right: 1px dotted black;">105</td><td style="width: 91px; border-right: 1px dotted black;"></td><td></td></tr></tbody></table></div>
The only way I can image doing this is placing an element outside the table and having a container around the table and the element. Then placing the element using position absolute on top of the table.
div {
width: 200px;
position: relative;
}
table {
width: 200px;
}
td {
border: 1px solid red;
height: 40px;
}
span {
position: absolute;
padding: 2px;
z-index: 99;
}
<div>
<span>Lorem ipsum dolor sit amet</span>
<table>
<tr>
<td></td>
<td></td>
</tr>
</table>
</div>
How about changing your html layout? Try to use after pseudo element and position:absolute. This technique saves me in a lot of situation and it's very strong, I think.
div {
border: 1px solid green;
padding: 2px;
position: relative;
width: 150px;
}
div:after {
background: green;
bottom: 0;
content: '';
display: block;
left: 50%;
position: absolute;
top: 0;
transform: translateX(-50%);
width: 1px;
}
<div>
This text should cross two td´s
</div>
table {
border: 1px solid black;
}
td {
border: 1px solid red;
}
th {
text-align:center;
}
<table>
<tr>
<th colspan="2">Monthly Savings</th>
</tr>
<tr>
<td width="50px">This text should cross two td´s</td>
<td width="50px"></td>
</tr>
</table>
You can include the border will be visible.
All the best. For any query please comment.

CSS: not exact vertical align in table

I find that if I put an image inside a table cell like this (JSFiddle):
<table style="height: 300px; border: 1px solid black">
<tr>
<td><img src="https://www.google.com.hk/images/srpr/logo11w.png" /></td>
</tr>
</table>
There will be a small space below the image, making the vertical align not exact:
Does any one know what is happening here?
I tried to add vertical-align: middle to the td, but it makes no difference.
Have you tried adding display: block to the img element? Seems to fix most problems for things within tables.
img {
display: block;
}
<table style="height: 300px; border: 1px solid black">
<tr>
<td>
<img src="https://www.google.com.hk/images/srpr/logo11w.png" />
</td>
</tr>
</table>
JSFiddle
You have to set the img as "display:block"
img {display:block}
http://jsfiddle.net/91beLce7/4/
Try this Fiddle
*{
margin: 0;
padding: 0;
}
table tr td img{
display: block;
}
You can fix that with line-height: .8em;
Try like this: Demo
* {
margin: 0;
padding: 0;
}
table {
background:red;
height: 300px;
border: 1px solid black;
}
tr {
background:#ccc;
}
img {
background:green;
display: block;
}

CSS Positioning with Text and Images on Same Line

How to I align my text and image on the same line?
Whenever I used padding or margins it crashes into the circle image I'm using.
#alignPhoto {
padding-right: 50px;
padding-left: 400px;
}
#alignCompany {
margin-left: 240px
}
#alignImage {
position: relative;
bottom: -250px;
}
.wrapper {
background: #C3C3C3;
padding: 20px;
font-size: 40px;
font-family: 'Helvetica';
text-align: center;
position: relative;
width: 600px;
}
.wrapper:after {
content: "";
width: 200px;
height: 0;
border-top: 42px solid transparent;
border-bottom: 42px solid transparent;
border-right: 40px solid white;
position: absolute;
right: 0;
top: 0;
}
<div id="alignPhoto">
<div class="circle" id=image role="image">
<img src="http://placehold.it/42x42">
</div>
</div>
<div id=alignPhoto class="titleBoldText">Mary Smith</div>
<div id=alignCompany class="titleText">Morris Realty and Investments</div>
<br>
Currently It does this:
My desired effect is this:
Any help or suggestions would be greatly appreciated.
You're making it a little more complicated than it needs to be. Just put two elements as wrappers (one you already have in alignImage, set them to display as inline-block and then put the vertical-align to middle, top, or whatever you like. I got rid of all the bizarre padding, which was messing with the display as well. Looks like that was a holdover from your vertically stacked layout.
Edit – You've also got two elements with the ID alignPhoto. You really, really shouldn't do that. If you need to style two different elements with one rule, please use classes instead.
#alignPhoto {
display: inline-block;
vertical-align: middle;
}
#alignPhoto img {
border-radius: 100%;
}
#alignImage {
position: relative;
}
.alignText {
display: inline-block;
vertical-align: middle;
}
.titleBoldText { text-align: right; }
<div class="alignText">
<div class="titleBoldText">Mary Smith</div>
<div id=alignCompany class="titleText">Morris Realty and Investments</div>
</div>
<div id="alignPhoto">
<div class="circle" id=image role="image">
<img src="http://placehold.it/42x42">
</div>
</div>
<br>
One quick and dirty way to wrap it in a table, as to get your vertical align working without any problems as well.
<table>
<tr>
<td>
<div id="alignPhoto" class="titleBoldText">Mary Smith</div>
<div id="alignCompany" class="titleText">Morris Realty and Investments</div>
</td>
<td>
<img src="image/url" alt=""/>
</td>
</tr>
</table>
http://jsfiddle.net/7m5s6gd7/
What about slightly simpler version:
HTML:
<div id="alignPhoto">
<div class="content-wrapper">
<p>Mary Smith</p>
<p>Morris Realty and Investments</p>
</div>
<div class="image-wrapper" id="image" role="image">
<img src="http://placehold.it/250x200" />
</div>
</div>
CSS:
.content-wrapper { float:left; }
.image-wrapper img { border-radius:50%; }
#alignPhoto {
display: flex;
flex-direction: row;
align-items: center;
}
JSFiddle for that
Basically you keep both paragraphs of text in one holding div and float it to left. This alone should do the job.
EDIT:
To make it even simpler, you can use flexbox for vertical alignment.
I've updated the answer.
One of the more effective and scalable solutions to ensuring elements are placed correctly from left to right are to employ wrapper divs with clear:both;. Inside of these wrapper divs you can use float:left or float:right. The wrapper divs allow you to generate a new "row".
#alignPhoto {
float: left;
margin-left: 10px;
}
#profileCompany, #profileName {
display:block;
width:100%;
}
#alignImage {
float: left;
}
.profileWrapper {
float:left;
}
/* Below creates a circle for the image passed from the backend */
.wrapper {
padding: 20px;
font-family: 'Helvetica';
text-align: center;
position: relative;
width: 600px;
clear: both;
}
.profileWrapper:after {
content: "";
width: 200px;
height: 0;
border-top: 42px solid transparent;
border-bottom: 42px solid transparent;
border-right: 40px solid white;
/* Tweak this to increase triangles height */
position: absolute;
right: 0;
top: 0;
}
.circle {
display: block;
height: 50px;
width: 50px;
background-color: #cfcfcf;
-moz-border-radius: 25px;
-webkit-border-radius: 25px;
-khtml-border-radius: 25px;
border-radius: 25px;
}
<div class="wrapper">
<div class="profileWrapper">
<div id=profileName class="titleBoldText">Mary Smith</div>
<div id=profileCompany class="titleText">Morris Realty and Investments</div>
</div>
<div id="alignPhoto">
<div class="circle" id=image role="image">
</div>
</div>
</div>

Fixed table layout overflow to the side

Problem
I have a fixed width table (which it must be) and one of the cells contains text
that is too long to fit within it, so it overflows outside the cell to the right.
I need to have all the table cells' text to be aligned to the right.
I ideally don't want to change any of the markup.
What I'm Looking For
I'm in need of finding someway for the (text in the example) "longlonglong" to overflow to the left over the other previous cells and maintain it's aligned right state.
Code
HTML
<table width="120">
<tr>
<td width="30">text</td>
<td width="30">text</td>
<td width="30">text</td>
<td width="30">very longlonglong text</td>
</tr>
</table>
CSS
td {
text-align: right;
border: 1px solid black;
vertical-align: top;
}
table {
border: 1px solid red;
table-layout: fixed;
}
Example
http://jsfiddle.net/xareyo/eVkgz/
See http://jsfiddle.net/eVkgz/1/
<table width="120">
<tr>
<td width="30">text</td>
<td width="30">text</td>
<td width="30">text</td>
<td width="30">
<div id="container1">
<div id="container2">very longlonglong text</div>
</div>
</td>
</tr>
</table>
td {
text-align: right;
border: 1px solid black;
vertical-align: top;
}
#container1 {
width: 30px;
position: relative;
}
#container2 {
float: right;
overflow: visible;
text-align: right;
}
table {
border: 1px solid red;
table-layout: fixed;
}
Do you need a variable height of the cells?
If not:
Place a div inside the td and this CSS:
td {
text-align: right;
border: 1px solid black;
vertical-align: top;
position: relative;
}
td div {
position: absolute;
right: 0;
}
table {
border: 1px solid red;
table-layout: fixed;
}
Add word-break: break-all; to yours td style:
td {
word-break: break-all;
text-align: right;
border: 1px solid black;
vertical-align: top;
}

Resources