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 above example, 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 a 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 calculus
  • 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: content: "A very long publication title..."}
      @top-right { content:  "The long organization name..." " " counter(page)}
}