Creating a printable page is relatively easy - at its simplest, design a page and don't display any Layout Elements, and then use the browser to print the page. The browser will format it for a given page size and add some default heads and footers. However if you need finer control over the content, particularly for documents which span over multiple pages, more advanced approaches are needed.
In this we describe how to construct a document that has:
- A Table of Contents at the start
- Custom page headers
- An Index page at the end
Note, the example here is simplified to make it clear how the mechanism works. In typical use, you can create long and complex documents, and it would be normal to use Query Embeds to pull content from elsewhere with the site - for example allowing the content to be edited within Data Entry Forms and stored in a Table.
Method
To work around the current limitations in browser pagination and cross-referencing support, we first upload two scripts.
Add an Upload Component, and note the page number assigned to it in the site - we will need this later.
Upload into it the following two javascript files:
Now create a Page component that will contain your document.
In the Behavior Editor for this page:
Set it to not display any Layout Elements
Set the Script <head> Start to:
<!--
<script type="text/javascript">
window.onload = function(){
var scriptEl = $dce('script');
scriptEl.setAttribute('src', 'https://unpkg.com/pagedjs/dist/paged.polyfill.js');
var headEl = $dget('head')[0];
headEl.appendChild(scriptEl);
};
</script>
-->
<script type="text/javascript" src="../NNNN/files/createIndex.js"></script>
<script type="text/javascript">
/*
This script creates page content using paged.js
If the table of contents page numbers do not appear after the items, check that the page width
of pseudobody is not wider than it's container and the numbers are getting clipped
*/
window.onload = function(){
g_ncPageHlpr.addPrintEntities('paged-print-ready');
// ----- for indexing -----
window.PagedConfig = {
before: function () {
createIndex({
spanClassIndex: "book-index",
indexElement: "#book-index",
alphabet: true,
});
},
};
// ----- convert to pages -----
__addScript('../NNNN/files/paged-print-ready.js');
function __addScript(src){
var scriptEl = $dce('script');
scriptEl.setAttribute('src', src);
var headEl = $dget('head')[0];
headEl.appendChild(scriptEl);
}
};
</script>
Substituting the two NNNN highlighted above with the number of the Upload Component
Set the Scripts, <head> General to:
<style>
.link-page a::after {
content: target-counter(attr(href), page);
}
.link-page::after {
content: ", ";
}
.link-page:last-child::after{
content: none;
}
.index-value::after {
content: " – ";
}
#list-index-generated {
list-style-type: none;
}
.list-alphabet-element {
font-weight: bold;
padding-top: 18px;
padding-bottom: 9px;
font-family: Arial, Helvetica, sans-serif;
}
.index-value {
display: inline-block;
min-width: 120px;
}
.index-value:first-letter {
text-transform: uppercase;
}
.index-value::after {
content: none;
}
.link-page a {
text-decoration: none;
color: currentColor;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
@page {
size: A4;
}
#table-of-contents {
page: frontmatterLayout;
}
#standard {
page: standard;
}
@page frontmatterLayout:left {
@top-left{
content: counter(page, lower-roman) "\00A0 \00A0 \00A0 \00A0 \00A0 \00A0 ";
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
padding-top:10px;
}
@top-right{
content: string(section);
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
width:300px;
padding-top:10px;
}
}
@page frontmatterLayout:right {
@top-right{
content: " \00A0 \00A0 \00A0 \00A0 \00A0 \00A0" counter(page, lower-roman) ;
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
padding-top:10px;
}
@top-left{
content: string(title);
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
width:300px;
padding-top:10px;
}
}
@page standard:first {
@top-left{
content:" ";
font-family: Arial;
font-size: 10pt;
background-color:transparent;
height:50px;
border-bottom: 0px solid black;
}
@top-right{
content: " ";
font-family: Arial;
font-size: 10pt;
background-color:transparent;
height:50px;
border-bottom: 0px solid black;
}
}
@page standard:left {
@top-left{
content: counter(page) "\00A0 \00A0 \00A0 \00A0 \00A0 \00A0 ";
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
padding-top:10px;
}
@top-right{
content: string(section);
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
width:300px;
padding-top:10px;
}
}
@page standard:right {
@top-right{
content: " \00A0 \00A0 \00A0 \00A0 \00A0 \00A0" counter(page) ;
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
padding-top:10px;
}
@top-left{
content: string(title);
font-family: Arial;
font-size: 10pt;
color:#CC6600;
border-bottom: 1px solid #CC6600;
height:40px;
width:300px;
padding-top:10px;
}
}
.startbody{
break-before: page;
}
.pagebreak{
break-before: page;
}
.pagebreakafter{
break-after: page
}
.pagebreakright{
page-break-before: right;
}
.pagebreakafterright{
page-break-after: right;
}
.resetpage{
counter-reset: page 1;
}
.chapter {
break-before: page;
}
.toclink::after {
content: target-counter(attr(href url), page);
padding-left: 5px;
float:right;
}
</style>
Now edit the page itself. we will be adding some classes and HTML markup to the page, so go to Source mode, and paste in:
<div id="table-of-contents">
<p>Table of Contents</p>
<p><a class="toclink" href="#SectionA"><span>Section A</span></a></p>
<p><a class="toclink" href="#SectionB"><span>Section B</span></a></p>
<p><a class="toclink" href="#SectionC"><span>Section C</span></a></p>
</div>
<div id="standard">
<p class="pagebreak" id="SectionA">This is Section A</p>
<p>text <span class="book-index" data-book-index="Apple">some apples</span></p>
<p class="pagebreak" id="SectionB">This is Section B</p>
<p>text <span class="book-index" data-book-index="Pear">a bowl of pears</span></p>
<p class="pagebreak" id="SectionC">This is Section C</p>
<p>text <span class="book-index" data-book-index="Peach">a pie made of peaches</span></p>
</div>
<div id="book-index">Index</div>
Save this, and now view the page.
It should render this as a single page, but with page numbers added to the entries within the table of contents, and some header lines where page breaks might be expected to show.
Now use the browser's Print dialog to see a print preview of the page. This should now show it divided up into multiple pages.
If you want a PDF, you can print to PDF, or you can use the Create PDF File action to have the system generate a PDF and store it in a File field.
Examining the HTML of the page as detailed above, note:
For the Table of Contents, you need a div with an id of "table-of-content"
Within that, each entry should have a class of "toclink" and contain a link to a bookmark elsewhere in the page where that section of the document is located.
Place the main body of the document within a div with an id of "standard". This sets up the page headers with page numbers.
Where you want the content to start on a new page, have an element with a class="pagebreak". This is only needed where you want to force a page break: if you have more content within a section than will naturally fit on a page it will handle that automatically.
Within the page contents body, add an id= with the same name matching the href=# in the table of contents.
Where you want some text to appear in the index, surround that text with a span, and give it a class=book-index and a data-book-index of the text that you want to appear within the index
Then the div with an id=book-index defines where you want the index to be rendered.
For the example above, it should then create entries that read:
A
Apple 2
P
Peach 4
Pear 3
If the same entry appears on several pages the index will show it in a comma separated list.