Page Headers and Footers
Page Headers and Footers - Built-in CSS
How to Change the Size of Headers and Footers
@page {
margin-top:3cm !important;
margin-bottom:3cm !important;
margin-left:2cm !important;
margin-right:2cm !important;
}
@page {
@top-left {
content:"..."
padding: 1cm;
}
..
}
How to Change the Font of the Headers and Footers
@page {
font-size: 12pt;
font-family: "Arial";
}
@page table-of-contents {
font-size: 12pt;
font-family: "Arial";
}
How to Display Chapter's Headers on First Page
/* No headers on the chapter first page. */
@page chapter:first:left{
@top-left {
content: none;
}
}
@page chapter:first:right{
@top-right {
content: none;
}
}
@page chapter:first:left{
@top-left {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
}
}
@page chapter:first:right{
@top-right {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
}
}
How to Position Text in the Headers and Footers
@page :left {
@top-left {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
}
}
@page :right{
@top-right {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
}
}
@page :left {
@top-left {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
}
@top-left-corner {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
white-space: nowrap;
text-align:left;
}
}
@page :right{
@top-right {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
}
@top-right-corner {
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
white-space: nowrap;
text-align:right;
}
}
How to Change the Header Separators
strings
defined for parts, chapters, and
sections. Each of these strings start with the " | " character as a separator. For example, in
the header of a page, you may find a sequence of
strings:My Publication | Introduction | Getting Started
- "My Publication" is the value of the
maptitle
string. - "Introduction" is the value of the
chaptertitle
string. - "Getting Started" is the value of the
sectiontitle
string.
There might be cases where you want to change this separator. You will need to recompose the header content using the above string sets. Suppose you want to use " - " as a separator. In your customization CSS, add the following CSS rule:
*[class ~= "topic/topic"][is-part] > *[class ~= "topic/title"] {
string-set: parttitle " - " counter(part, upper-roman) " - " content(),
parttitle-no-prefix " " counter(part, upper-roman) " - " content(),
chaptertitle "",
chaptertitle-no-prefix ""; /* Avoid propagating a past chapter title on a new part */
}
*[class ~= "topic/topic"][is-chapter]:not([is-part]) > *[class ~= "topic/title"] {
string-set: chaptertitle " - " counter(chapter) " - " content(),
chaptertitle-no-prefix " " counter(chapter) " - " content();
}
If you enabled the deep numbering for chapters and subsections, then use:
*[class ~= "map/map"][numbering ^= 'deep'] *[class ~= "topic/topic"][is-part] > *[class ~= "topic/title"] {
string-set: parttitle " - " counter(part, upper-roman) " - " content(),
parttitle-no-prefix " " counter(part, upper-roman) " - " content(),
chaptertitle "",
chaptertitle-no-prefix ""; /* Avoid propagating a past chapter title on a new part */
}
*[class ~= "map/map"][numbering ^= 'deep'] *[class ~= "topic/topic"][is-chapter]:not([is-part]) > *[class ~= "topic/title"] {
string-set: chaptertitle " - " counters(chapter-and-sections, ".") " - " content(),
chaptertitle-no-prefix " " counters(chapter-and-sections, ".") " - " content(),
sectiontitle ""; /* Avoid propagating a past section title on a new chapter */
}
*[class ~= "map/map"][numbering ^= 'deep'] *[class ~= "topic/topic"][is-chapter]:not([is-part]) > *[class ~= "topic/topic"] > *[class ~= "topic/title"] {
string-set: sectiontitle " - " counters(chapter-and-sections, ".") " - " content();
}
How to Simplify the Header (Keep Only the Chapter Title)
The headers display information such as map title, part title, chapter title, and section title, ending in the page number.
content: string(maptitle) string(parttitle) string(chaptertitle) string(sectiontitle) " | " counter(page);
This might be too much if you have long titles. The solution is to override the default header content.
In your customization CSS, add the following CSS rule:
@page :left {
@top-left {
content: string(chaptertitle) " | " counter(page);
}
}
@page :right{
@top-right {
content: string(chaptertitle) " | " counter(page);
}
}
- Try to also specify the name of the page, to increase the specificity of the rules:
@page :left, table-of-contents:left, chapter:left{ ... } @page :right, table-of-contents:right, chapter:right{ ... }
- Add an
!important
classifier just before the semi-colon.@top-right { content: string(chaptertitle) " | " counter(page) !important; }
How to Style a Part of the Text from the Header
- Use an SVG image as the background for a page margin box or for the entire page. See: How to Add a Background Image to the Header.
- Use the
oxy_label
constructor. This is a function that creates a text label with a set of styles.
You can combine the@page { @top-right { content: oxy_label(text, "My Company", styles, "color:red; font-size: larger;") ' ' oxy_label(text, "Product", styles, "color:blue; text-decoration:underline;")); } }
oxy_label
withoxy_xpath
, to extract and style a piece of text from the document:content: oxy_label(text, oxy_xpath("/some/xpath"), styles, "color:blue; "));
Note: These functions work only with the Chemistry CSS processor.Note: You cannot usestring()
inside anoxy_label()
. As a workaround, to apply styling on the dynamic text retrieved by astring()
function you can define some overall styles for the entire page margin box and then use theoxy_label
to style differently the static text.@page { @top-right { color: red; content: oxy_label(text, "My Company", styles, "color:black") ' ' string(chaptertitle); /* This inherits the styling from @top-right*/ } }
- Use two adjacent page margin boxes, and style them
differently:
@page { @top-center { content: "First part"; color: red; text-align:right; } @top-left { content: "- Second part"; color: blue; text-align:left; } }
How to Add a Background Image to the Header
A common use-case is to add a background image to one of the page corners.
@page :left {
@bottom-left-corner{
content: " ";
background-image: url('https://www.oxygenxml.com/resellers/resources/OxygenXMLEditor_icon.svg');
background-repeat:no-repeat;
background-position:50% 50%;
}
}
content
property. If not, the page
margin box will not be generated. Another use-case is to use the @top-left
or @top-right
page
margin boxes. These boxes have an automatic layout and they can be very small if they have no
content. If there is no text to be placed over the image, use a series of non-breaking spaces
(\A0
) to increase the box width as in the following example (alternatively,
you can use the technique described in How to Decorate the Header by Using a Background Image on the Entire Page):
@page :left {
@top-left{
content: '\A0\A0\A0\A0\A0\A0\A0\A0\A0\A0';
background-image: url('https://www.oxygenxml.com/resellers/resources/OxygenXMLEditor_icon.svg');
background-repeat:no-repeat;
background-position:50% 50%;
}
}
How to Decorate the Header by Using a Background Image on the Entire Page
@page :left, chapter:left, chapter:first:left {
background-image: url('img/page_background_image_with_logos_and_artwork_for_left_page.svg');
background-repeat: no-repeat;
background-position: 50% 50%;
background-size: 8.5in 11.5in; /* Optional: Adapt to your page size. */
}
How to Change Header Text for Each Topic
It is possible to dynamically change the header depending on the content in a topic. The
following example assumes that the data to be presented in the header is located in the
metadata section of each topic. One way is to specify it in the DITA map is by using the
<topicmeta>
element for the <topicref>
topic
reference:
...
<topicref href="topics/installing.dita">
<topicmeta>
<data name="header-data" value="ID778-3211"/>
</topicmeta>
...
In the above example, there is set of key value pairs with the name
header-data
. This information is automatically copied into the content in
the merged map
file, like this:
<topic ... >
<title class="- topic/title ">Installing</title>
<shortdesc class="- topic/shortdesc ">You install components to make them available for your
solution.</shortdesc>
<prolog class="- topic/prolog ">
...
<data class="- topic/data " name="header-data" value="ID778-3211"/>
...
This information can be extracted from the CSS:
/* Define the string set variable that contains the text extracted from the data element */
*[class ~= "topic/topic"] *[class ~= "topic/data"][name="header-data"] {
string-set: hdrstr attr(value);
}
/* Using the value='none' stops applying the image. */
*[class ~= "topic/topic"] *[class ~= "topic/data"][name="header-data"][value="none"] {
string-set: hdrstr "";
}
/* Use the string set variable in one of the page margin boxes. */
@page chapter {
@top-left-corner {
content: string(hdrstr);
}
}
...
<topicref href="topics/installing.dita">
<topicmeta>
<data name="header-data" value="ID778-3211"/>
</topicmeta>
</topicref>
<topicref href="..."> <!-- Uses the same value -->
<topicref href="..."> <!-- Uses the same value -->
<topicref href="..."> <!-- Uses the same value -->
<topicref href="topics/change.dita">
<topicmeta>
<data name="header-data" value="ID990-3200"/>
</topicmeta>
</topicref>
<topicref href="..."> <!-- The string set is changed now -->
<topicref href="..."> <!-- The string set is changed now -->
<topicref href="..."> <!-- The string set is changed now -->
none
value:...
<topicref href="..."> <!-- The string set is void now -->
...
How to Change Header Images for Each Chapter
It is possible to dynamically change an image in the header depending on the chapter. For
this, you need to define an image reference in the metadata section of each chapter. One way
is to specify it in the DITA map by using the <topicmeta>
element for
the <chapter>
topic reference:
...
<chapter href="topics/installing.dita">
<topicmeta>
<data name="header-image" value="img/installing.png"/>
</topicmeta>
...
In the above example, there is set of key value pairs with the name
header-image
. The img/installing.png
is an image reference
relative to the DITA map URI. This information is automatically copied into the content in the
merged map
file, like this:
<topic is-chapter="true" ... >
<title class="- topic/title ">Installing</title>
<shortdesc class="- topic/shortdesc ">You install components to make them available for your
solution.</shortdesc>
<prolog class="- topic/prolog ">
...
<data class="- topic/data " name="header-image" value="img/installing.png"/>
...
This information can be picked up from CSS:
/* Define the string set variable that contains an URL */
*[class ~= "topic/topic"] *[class ~= "topic/data"][name="header-image"] {
string-set: imgst oxy_url(oxy_xpath('/*/@xtrf'), attr(value));
}
/* Using the value='none' stops applying the image. */
*[class ~= "topic/topic"] *[class ~= "topic/data"][name="header-image"][value="none"] {
string-set: imgst "";
}
/* Use the string set variable in one of the page margin boxes. */
@page chapter {
@top-left-corner {
content: string(imgst);
font-size:0; /* remove the font ascent and descent */
}
}
Details: The @value
attribute is used to build a URL relative to the
URI of the DITA map. To determine the base URI of the DITA map, the @xtrf
attribute was used from the root element of the merged map document, extracted using the
oxy_xpath
function.
- The image is always aligned vertically to the middle of available space from the page margin box.
- Make sure you use an image of the correct size. For example, if you want to place the image in the top-left corner of the page, assuming the top and left page margins are 1 in, then make sure the image is a square having a size of 1 in.
-
The image is applied to all pages that follow the data element, until another data element changes it:
... <chapter href="topics/installing.dita"> <topicmeta> <data name="header-image" value="img/installing.png"/> </topicmeta> </chapter> <chapter href="..."> <!-- Uses the same installing.png image --> <chapter href="..."> <!-- Uses the same installing.png image --> <chapter href="..."> <!-- Uses the same installing.png image --> <chapter href="topics/change.dita"> <topicmeta> <data name="header-image" value="img/change.png"/> </topicmeta> </chapter> <chapter href="..."> <!-- Uses the same change.png image --> <chapter href="..."> <!-- Uses the same change.png image --> <chapter href="..."> <!-- Uses the same change.png image -->
To clear the image, use thenone
value:... <data name="header-image" value="none"/> ...
How to Add a Multi-line Copyright Notice to the Footer
@page :right{
@bottom-center {
content: "© 2017 - My Company Ltd \A All rights reserved";
font-size: 0.5em;
color: silver;
}
}
How to Add a Group of Topics to the Footer
How to Add a Link in Headers and Footers
How to Change the Header Styling Depending on Page Side
To modify the styling of the default page headers, add the following CSS rule in your customization CSS:
@page :left {
@top-left {
color:navy;
font-style:italic;
}
@top-right {
color:red;
}
}
If you intend to modify just the headers of the table of contents, use the
table-of-contents
page rule selector:
@page table-of-contents:left {
@top-left {
color:navy;
font-style:italic;
}
@top-right {
color:red;
}
}
How to Use XPath Computed Data or Images in the Header or Footer
A very simple approach is to use the oxy_xpath
directly in the
content
property:
@page front-page {
@top-center {
content: "Created: " oxy_xpath('//*[contains(@class, " topic/created "][1]');
}
}
Example 1: Compute the Number of Words
The following example computes the number of words from the publication. It counts all the words, including the ones from the TOC, but does not take the static labels into account:
@page front-page {
@bottom-center {
content: "Number of words: "
oxy_xpath("string-length(normalize-space(/)) - \
string-length(translate(normalize-space(/),' ','')) +1");
}
}
/
or //
. This is different from the case when the
oxy_xpath
is used in CSS rules that match an element. In this case, the
XPath expressions are evaluated in the context of the matched element and you can use relative
paths.Example 2: Retrieve Image from a Document and Insert it in the Header
<bookmeta>
<metadata>
...
<data name="cover">
<image href="product-cover.png" outputclass="cover-image"/>
</data>
...
</metadata>
</bookmeta>
@page {
@top-center {
content: url("oxy_xpath('//*[contains(@outputclass, "cover-image")]/@href')");
}
}
If the URL returned by oxy_xpath
is not absolute, it is considered to be
relative to the CSS file. To obtain an absolute URL from one relative to the XML document,
you can use in the XPath expression functions like resolve-uri
and
document-uri
:
@page {
@top-center {
content: url(oxy_xpath("resolve-uri(//*[contains(@outputclass, 'cover-image')]/@href), document-uri(/))"));
}
}
Example 3: Insert the Current Date in the Footer
Another example is to use the oxy_xpath
function to compute the current
date and insert it in the publication footer:
@page {
@bottom-left {
content: oxy_xpath('current-date()');
}
}
Example 4: Picking up Metadata from the Original Map
Another example is to use the oxy_xpath
function to extract the title, or
any other element text value from the original processed DITA map file. For this, you can
use the @xtrf
attribute that is set on the root element of the merged map.
This attribute contains the URL of the input map.
:root{
string-set: maptitle oxy_xpath('document(@xtrf)/*[contains(@class, " map/map ")]/*[contains(@class, " topic/title ")]/text()');
}
How to Add a Line Under the Header
There are two ways to add a horizontal line under the header.
Method 1: Add a Border in the Page Margin Boxes
To add a horizontal line that would stretch across the width of the page, add a bottom
border to each of the 5 margin boxes in the top side of the page
(top-left-corner
, top-left
, top-center
,
top-right
, top-right-corner
).
If you consider that the space between the header and the bottom border is too large, you
could also change the alignment by adding a vertical-align: bottom;
declaration in the page margin boxes.
@page chapter, chapter:first:left:right, front-page{
padding-top: 1em;
@top-left {
content: "Custom header";
color: gray;
border-bottom: 1px solid black;
vertical-align: bottom;
}
@top-center{
content:" ";
border-bottom: 1px solid black;
vertical-align: bottom;
}
@top-right{
content:" ";
border-bottom: 1px solid black;
vertical-align: bottom;
}
@top-right-corner{
content:" ";
border-bottom: 1px solid black;
vertical-align: bottom;
}
@top-left-corner{
content:" ";
border-bottom: 1px solid black;
vertical-align: bottom;
}
padding-top: 1em;
is used to avoid the border at the bottom of
the header that joins with the page content.Method 2: Use a Background Image
An alternative method is to add a horizontal line/border under an existing header (or in any other part of the page) using an SVG image, as described in How to Add a Background Image to the Header.
How to Change the Headings Using a Parameter
Suppose you need to change the headings of your publication by specifying a static text in a parameter.
First, establish a name for your parameter (it must start with the
args.css.param.
prefix). For example, you could name it
args.css.param.heading.text
. It will have the text value that you will pass
when starting the transformation. This parameter does not have to be registered anywhere as it
will be automatically recognized and passed as an XML attribute on the root of the merged
file, as specified in Styling Through Custom Parameters.
Next, alter your customization CSS to make use of the parameter value. In the example below, the text is placed in the central part of the header:
@page front-page, table-of-contents, chapter {
@top-center{
content: oxy_xpath("/*/@heading.text");
}
}
The text does not affect the first pages from the page sequences because the built-in CSS page rules clear the
content from the headers. If you need the text content on all pages, you might consider adding
an !important
keyword after the content
property value, or
increase the specificity of the page selectors, like this:
@page front-page,
table-of-contents,
table-of-contents:first:left,
table-of-contents:first:right,
chapter:first:left,
chapter:first:right{
@top-center{
...
}
}
Another use case is to alter the string-sets that are used in the headers (not the headers directly), as it is explained here: How to Use XPath Computed Data or Images in the Header or Footer. You can use this technique to alter the chapter titles as in the following example:
*[class ~= "map/map"][numbering^='deep']
*[class ~= "topic/topic"][is-chapter]:not([is-part]) >
*[class ~= "topic/title"] {
string-set:
chaptertitle " | " counters(chapter-and-sections, "." ) " - " oxy_xpath("/*/@heading.text") content(),
sectiontitle "";
}
How to Change the Headings depending on the Language
It is possible to customize the text displayed in the headings depending on the language of the publication.
In this case, you can simply use of the @lang
attribute in your customization
CSS. In the following example, the page counter displayed in the bottom part of the page is
preceded by the word "Page", according to the selected language:
@page chapter {
@bottom-center {
content: oxy_xpath("if (@lang='es') then 'Página' \
else if (@lang='it') then 'Pagina' \
else 'Page'") " " counter(page);
}
}
How to Display the Chapter and the Page Number in the Footer
*[class ~= "map/map"] *[class ~= "topic/topic"][is-part] {
string-set: chapternumber "";
}
*[class ~= "map/map"] *[class ~= "topic/topic"][is-chapter]:not([is-part]) {
string-set: chapternumber counter(chapter, decimal-leading-zero);
}
*[class ~= "map/map"] *[class ~= "bookmap/frontmatter"]
*[class ~= "map/map"] *[class ~= "bookmap/backmatter"]
*[class ~= "map/map"] *[class ~= "topic/topic"][is-part] ~ *[class ~= "topic/topic"]:not([is-part]) {
string-set: chapternumber "";
}
...
@page chapter {
@bottom-center {
content: string(chapternumber) "-" counter(page, decimal-leading-zero);
}
}