I currently am creating a perfectly good PDF. there is nothing technically wrong with it. However, the TOC is ugly.
The TOC is generated via xsl which is passed through jinja2 for simple details to the top section of the page. I have modified the XSL to match the client's branding and design precisely. However, the list keeps growing in height.
Here is the current result (sorry to blur the text) you can see the toc picks up at the right spot on the new page, but there seems to be no way to apply a top margin to the new page:
The code: Here is the xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:outline="http://wkhtmltopdf.org/outline"
xmlns="http://www.w3.org/1999/xhtml">
<xsl:output doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
indent="yes" />
<xsl:template match="outline:outline">
<html>
<head>
<title>Table of Contents</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
body{
background-color: #fff;
margin-left: 0px;
margin-top: 0px;
color:#1e1e1e;
font-family: arial, verdana,sans-serif;
font-size: 90px;
}
.contentSection{
position:relative;
height:3200px;
width:6100px;
}
.profile{
position:absolute;
display:inline-block;
top:200px !important;
}
h1 {
text-align: left;
font-size: 70px;
font-family: arial;
color: #ef882d;
}
li {
border-bottom: 1px dashed rgb(45,117,183);
}
span {float: right;}
li {
list-style: none;
margin-top:30px;
}
ul {
font-size: 70px;
font-family: arial;
color:#2d75b7;
}
ul ul {font-size: 80%; padding-top:0px;}
ul {padding-left: 0em; padding-top:0px;}
ul ul {padding-left: 1em; padding-top:0px;}
a {text-decoration:none; color: color:#2d75b7;}
#topper{
width:100%;
border-bottom:8px solid #ef882d;
}
#title{
position:absolute;
top:60px;
font-size:60px;
left:150px;
color:#666666;
}
h1, h2{
font-size:60px;
-webkit-margin-before: 0px;
-webkit-margin-after: 0px;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
}
#profile{
position:static;
-webkit-border-top-left-radius: 40px;
-webkit-border-bottom-left-radius: 40px;
-moz-border-radius-topleft: 40px;
-moz-border-radius-bottomleft: 40px;
border-top-left-radius: 40px;
border-bottom-left-radius: 40px;
right:-540px;
background-color: #2d75b7;
padding:4px;
padding-left:60px;
padding-right:250px;
color:#fff;
display:inline-block;
margin-top:200px;
float:right;
}
#room{
padding-top: 200px;
padding-left: 150px;
display:inline-block;
}
#section{
padding-left: 150px;
color: #ef882d;
text-transform: uppercase;
font-size:60px;
font-weight: bold;
display:inline-block;
margin-top: 30px;
margin-bottom: 5px;
}
#area{
padding-left: 150px;
font-size:60px;
color:#2d75b7;
margin-top: 15px;
}
#dims{
padding-left: 150px;
font-size:60px;
color:#2d75b7;
margin-top: 15px;
}
#toc{
width:50%;
margin-top:150px;
margin-left:300px;
}
</style>
<script>
var value = {{profile|e}};
</script>
</head>
<body>
<div class="contentSection">
<div id="title">A title here</div>
<div id="topper">
<div id="profile" class="profile">{{profile|e}}</div>
<div id="room"> {{profile|e}} </div>
<div id="area"> Revision Date </div>
<div id="dims"> {{area|e}} </div>
<div id="section">Table of Contents</div>
</div>
<div id="toc">
<ul><xsl:apply-templates select="outline:item/outline:item"/></ul>
</div>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="outline:item">
<! begin LI>
<li>
<xsl:if test="@title!=''">
<div>
<a>
<xsl:if test="@link">
<xsl:attribute name="href"><xsl:value-of select="@link"/> .
</xsl:attribute>
</xsl:if>
<xsl:if test="@backLink">
<xsl:attribute name="name"><xsl:value-of select="@backLink"/> . </xsl:attribute>
</xsl:if>
<xsl:value-of select="@title" />
</a>
<span>
<xsl:value-of select="@page" />
</span>
</div>
</xsl:if>
<ul>
<xsl:comment>added to prevent self-closing tags in QtXmlPatterns</xsl:comment>
<xsl:apply-templates select="outline:item"/>
</ul>
</li>
</xsl:template>
</xsl:stylesheet>
I have dealt with content overflows in other areas of the PDF using traditional HTML, JavaScript, and a document ready flag. The TOC however requires an XSL file instead.
I tried do this with nth-child css nth-child is ignored.
The question:
*Is there a way within wkhtmltopdf or python pdf-kit to deal with page breaks in the TOC specifically, and place a better margin top on the new page? is there a way to supply a TOC as a traditional html page so that I can do this with javaScript instead? *
Code review
I made a quick code review in your XSL (and CSS) file. Even if it doesn’t solve your problem, it help reproducing and understanding it. Here is my comments:
Your XSL has a typo:
<! begin LI>
is not a valid XML tab. Is it a comment?I prefer using the
concat()
XPath function to append characters directly. Because, if you re-indent your code, you may introduce extra whitespaces.So, I replaced:
By:
I added a
xs:if
to prevent generating an empty<ul>
if it is not necessary:I also fixed duplicate or mal-formed CSS entries, I replaced:
by:
If you target XHTML, the
<style>
tag has a mandatorytype
attribute. Same remark for the<script>
attribute.Reproducing the problem
It was a little hard to reproduce your bug, because of a lack of information. So I guess it.
First, I create a sample TOC file, which look like this:
outline.xml
This file contains 70 items so that I can see the page breaks.
To build the HTML and PDF I used your (fixed) XSL file and run pdfkit:
note: your page size looks very huge…
Solution
You are right, wkhtmltopdf doesn't take into account the margin in your CSS:
This is a normal behavior, consider for instance the header paragraphs (
h1
,h2
, etc.). A header can have a top margin in order to add white space between a paragraph and the following header, but, if the header starts a new page we want to get rid of the margin, and have the heading touching to top margin of the page.For your TOC, there is a solution. You can use
padding
(instead ofmargin
):Actually, the TOC content (
#toc
element) is fixed:So, you can reduce the
margin-top
to match your need, for instance: