Edit online

Most printed publications use page headers and footers for almost all of their pages. To define them, you will use the page margin boxes.

@page :first {
  @top-center {
    content: "How to Grow Flowers";
    font-size: smaller;
    color: gray;
  }
}

You can set content and style to multiple page margin boxes. You should place them in the same parent @page rule. In the example above, the text is displayed only in the top-center of the first page, due to the :first page selector.

Edit online

To display the title of the publication or the title of the current chapter, you will need to extract some content from the document and use it in one or more page margin boxes. This is possible by using a string-set property. It is similar to a variable that is initialized to content each time a specific element is matched.

In the following example, the text content of the <H1> element is extracted and used as a publication title and the <H2> element defines the chapter title:

h1 {
  string-set: publication_title content();
}

h2 {
  string-set: chapter_title content();
}
Important: To define multiple string sets for an element, use a single string-set property with a list of comma-separated definitions:
h1 {
  string-set: publication_title content(),  publication_author attr(data-author);
}

The following example uses the collected strings in the top margins of the pages. It joins the publication title and the chapter title by a "/" character, then places them in the outer side of the pages (to the left for the left-side pages, to the right for the right-side pages).

@page :left {
  @top-left { 
    content : string(publication_title) " / " string(chapter_title);
  }
}

@page :right {
  @top-right {
    content: string(publication_title) " / " string(chapter_title);
  }
}

A string set may contain static text, content from the document, attributes from the element, or counters. This is a more complex example, where a chapter number is added to the chapter_title string set:

h1 {
  counter-reset: chapter;
}

h2 {
  string-set: chapter_title "Chapter (" counter(chapter) ")" content();
  counter-increment: chapter;
}

Edit online

You can use the oxy_xpath CSS extension function to execute an XPath expression over the input document. The main advantage is that you can execute any XPath processing, including:
  • Document data retrieval
  • Mathematical calculations
  • If/then/else conditions
You can use oxy_xpath in values of all properties defined in a page rule.
Important: This technique is not standard and is guaranteed to work only with this processor.
Note: The XPath expression from the page rules is evaluated in the context of the document root element, so you will need to use absolute expressions starting with / 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.
Tip: XPath 2.0 is supported (not schema aware).

Suppose your document defines a creation date in a metadata section. This section may be anywhere in your document. To place the creation date in the center of the publication header:

@page {
   @top-center {
        content: "Created: " oxy_xpath("//div[@class='created']/text()");
   }
}

Another example is to use an image from the document in the publication header:

@page {
   @top-center {
        content: url(oxy_xpath('//img[@class='product-img']/@href'));
   }
}

If the URL returned by oxy_xpath is not absolute, it is considered to be relative to the CSS file. To obtain a URL relative to the XML document, you can use in the XPath expression functions, such as resolve-uri and document-uri:

@page {
   @top-center {
        content: url(oxy_xpath(resolve-uri(//img[@class='product-img']/@href, document-uri(/))));
   }
}

Edit online

Besides the CSS counters that can be set on elements (for numbering sections, lists, tables, etc.), the CSS paged media module defines two more counters:

page
This counter returns the number of the current page.
pages
The number of total pages from the publication.

These counters are automatically updated by the publishing processor and can be used from the page margin boxes.

@page {
    @bottom-center {
      content: "Page: " counter(page);
    }
}

Or if you need to obtain "Page 4 of 100", you simply use:

  content: "Page: " counter(page) " of " counter(pages);

You can format the page counter with styles such as decimal, roman, lower-roman:

 @page table-of-contents {
        @top-right {
            content: "Contents | " counter(page, lower-roman);
        }
 }
Note: Using different counter styles under the same page name (for instance, using lower-roman for the left page and decimal on the right page) is not supported.

Edit online

In typical usage, the sub-regions of the header (the page margin boxes @top-left, @top-center, @top-right) and footer (@bottom-left, @bottom-center, @bottom-right) are distinct regions. If you are specifying content for all of them, the content set in one does not use space from the others. Instead, it wraps lines in its own region.

@page {
      @top-left { content: "A very long publication title..."}
      @top-center { content: "The long organization name..."}
      @top-right { content: counter(page)}
}

This creates a sort of table with fixed equal-sized columns, with the text wrapping inside of them.

You may need to 'merge' the center and right sub-regions for the header so that the layout engine has more room for topic titles before it wraps the title to a new line.

The solution is to eliminate the center part from the header and footer (@top-center and @bottom-center), and move the content to one of the sides:

@page {
      @top-left { content: "A very long publication title..."}
      @top-right { content:  "The long organization name..." " " counter(page)}
}

Edit online

If you need to style a fragment of text (for example, a company slogan) with certain colors or font styles, you have several options:
  • Use an SVG image as the background for a page margin box or for the entire page.
  • Use the oxy_label constructor. This is a function that creates a text label with a set of styles.
    @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;"));
        }
    }
    You can combine the oxy_label with oxy_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 use string() inside an oxy_label(). As a workaround, to apply styling on the dynamic text retrieved by a string() function you can define some overall styles for the entire page margin box and then use the oxy_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;
      }
    }

Edit online

It is possible to dynamically change the images from the publication header or footer depending on the section.

For example, you want to place an image that describes the current section. Assuming that the titles of the sections define some metadata attribute pointing to some image file and the image references should be absolute URIs or relative to the input document.

<h2 -data-header-image="installation.png">
...
<h2 -data-header-image="configuring.png">
...

The CSS should define a string-set that extracts the attribute value and builds an image for it using the attr(.., url) or url function.

Important: The attr(name, url) function resolves the reference relative to the input XML document URI. The url(attr(name)) resolves the reference relative to the CSS file URI.
@page {
	margin: 1in;
	@top-right-corner {
		content: string(str);		
		font-size:0; /* Get rid of ascent and descent to avoid spaces around the image */
	}
}

h2 {
	string-set: str attr(-data-header-image, url); /* references relative to the input document */
}

Besides the attr function, you can add text:

...
        string-set: str "Section... " attr(-data-header-image, url);
...