W3C


Paged Media Properties for CSS3

W3C Working Draft 22 June 1999


This version: http://www.w3.org/1999/06/WD-css3-page-19990623
Latest version: http://www.w3.org/TR/css3-page
Authors: Robert Stevahn (rstevahn@boi.hp.com)

Copyright 1999 W3C (MIT, INRIA, Keio), All Rights Reserved. W3C liability, trademark, document use and software licensing rules apply. Your interactions with this site are in accordance with our public and Member privacy statements.

Abstract

This document proposes an extension to CSS to permit finer control over the paged presentation, both printed and online, of Web pages. Some of the features described are useful for other media as well. Included are properties for describing headers, footers, footnotes and endnotes. These features require other features described here, such as cross-references and page-based counters. In addition, page-dependent floating elements are described

Status of this document

This document forms one part of a modular set of Working Drafts which will, when complete, define the next level of CSS.

The W3C Membership and other interested parties are invited to review this public specification and report implementation experience. Please send comments to the publicly archived list www-style@w3.org (archive). We welcome experimental implementation experience reports, although the CSS Working Group will not allow early implementation to constrain its ability to make changes to this specification prior to final release.

This Working Draft may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use W3C Working Drafts as reference material or to cite them as other than "work in progress". A list of current W3C Recommendations and other technical documents can be found at http://www.w3.org/TR.

[Editor's note: All editor's notes use class "note" and should be red in color.]

Table of contents


1. Requirements

These requirements are the result of discussions on the CSS&FP WG mailing list.

1.1 Headers and footers

1.2 Footnotes and endnotes

1.3 Cross-references

It is sometimes convenient to use some of the features above, such as page numbers, attribute values and element content, as cross-references. For example, "See figure 3, pg. 5." Given a known element, we would like to be able to access the following:

In addition, we would like to access the same information about "the most recent" element with a specified element name. For paged media, we must be able to determine this from either the beginning or end of page.

Given this markup:

   <P>For more information, see <A HREF="#section32">section 32</A>.
   ...

   <H1><A NAME="section32">Section 32: The details</A></H1>
   ...

It should be possible to generate this output:

 (1)   For more information, see section 32.
 (2)   For more information, see page 41.
 (3)   For more information, see section 32 (page 41).
 (4)   For more information, see "Section 32: The details" on page 41.

1.4 Page-based floats

Floating elements promise to be a boon for layout on the web, in many cases eliminating the need for "table layout." But when printed, many designers would like "mirrored" layouts on opposing pages. For example, perhaps you're using a three column grid with important notes on the left, main body text in the center, and links or ads on the right. When printed, you might want this layout for left pages, and the mirrored version for right pages.


2. Margin Boxes

CSS2 introduced the 'page box' and the 'page area'. In order to support headers and footers, CSS3 introduces margin boxes. As seen in the diagram below, four distinct margin boxes are implicitly formed by the margin settings of the page box. These boxes are known as 'page-top', 'page-bottom', 'page-left' and 'page-right'. For the purpose of duplex printing, we also define the 'page-inside' and 'page-outside' pseudo-properties, which the user agent will appropriately map to 'page-left' or 'page-right' on a page by page basis. In a simplex printing situation, 'page-inside' maps to 'page-left', and 'page-outside' maps to 'page-right'.

To be replaced with a real diagram

+-----------------------------+  +---+---------------------+---+
|          page-top           |  |   |                     |   |
+---+---------------------+---+  |   +---------------------+   |
|   |                     |   |  | p |                     | p |
|   |                     |   |  | a |                     | a |
|   |                     |   |  | g |                     | g |
|   |                     |   |  | e |                     | e |
|   |     page area       |   |  | - |      page area      | - |
|   |                     |   |  | l |                     | r |
|   |                     |   |  | e |                     | i |
|   |                     |   |  | f |                     | g |
|   |                     |   |  | t |                     | h |
|   |                     |   |  |   |                     | t |
+---+---------------------+---+  |   +---------------------+   |
|         page-bottom         |  |   |                     |   |
+-----------------------------+  +-----------------------------+

          CSS Page Box                    CSS Page Box

Margin boxes are instantiated through the use of new 'at-rules' designed for this purpose. These at-rules must be nested inside of page declarations (@page rules). For example, the following rule establishes a page header containing the current date, right justified and vertically centered within the top margin box:

@page {size: 8.5in 11in;
       margin: 10%;
       @page-top {margin-right:   5%; /* extends half way into 10% page margin */
                  text-align:     right;
                  vertical-align: middle;
                  content:        date("%d %B %Y");
       }
}

All standard CSS box properties such as 'background', 'padding', 'border' and 'margin' apply to margin boxes. In order to support the independent alignment and styling of different header and footer components, up to three instances of each of the four margin boxes are allowed. For example, to include a left justified page number in the top margin box, you would add an additional rule:

@page-top-2 {margin-left:    5%;
             text-align:     left;
             vertical-align: middle;
             content:        "Page " counter(pages);}

The ability to include multiple margin box instances creates the potential for conflict. For example: the top portion of the left margin box overlaps the left portion of the top margin box; centered text might overlap right justified text; multiple background colors could be specified. There are no rules for resolving such conflicts. The behavior in such cases is unspecified.

You will notice the use of the 'vertical-align' property above. With regard to vertical alignment, margin boxes behave the same as CSS2 table cells.

Note: When a user agent has knowledge of the printable area of the actual printed page, text alignment within margin boxes should be adjusted to remain within the printable area.


3. Populating Margin Boxes

The 'content' property was introduced in CSS2, and applied only to the 'before:' and 'after:' pseudo-elements. In CSS3, use of the 'content' property is extended to margin boxes. CSS3 also introduces several new mechanisms for generating content, as outlined in this section.

[One requirement not explicitly covered in this section is the requirement for accessing the document creation date "and maybe more." This requirement is handled adequately by the named strings described below. If the author wants the creation date to be accessible, they must include this information somewhere in the document. This can easily be done using server side includes in many cases. We will investigate a mechanism for including the content of HTTP headers.]

3.1 Named Strings

CSS does not contain a general-purpose document query language. This significantly limits the ability of CSS to inspect document structure. On the other hand, CSS processing is mostly linear in nature and therefore has relatively high performance. Take, for example, CSS counters. When we want to generate text expressing a chapter number, we don't perform a query such as "How many H1 elements precede this element in the document?" Instead, we set a counter on H1 elements and then reference that counter in the appropriate 'before:' or 'after:' pseudo-element.

In order to support running headers and footers (e.g. placing the current chapter name in the header), we must use a similar approach. Instead of asking the document "What is the text content of the most recent H1 element?", we must be able to capture the element contents whenever we encounter an H1 element, and then refer to that text from within a margin box.

CSS3 introduces 'named strings', which are the textual equivalent of counters and which have a distinct namespace from counters. The following example captures the contents of H1 elements, which represent chapter names in this hypothetical document:

H1 {string-set: chapter content();}

When an H1 element is encountered, the 'chapter' string is set to the element contents, and the previous value of 'chapter', if any, is overwritten. Named strings follow the same nesting rules as counters. The 'string-set' property has the same legal values as the CSS2 'content' property, including the extraction of the current value of counters.

It's possible that the page in question itself contains an H1 element. Should the formatter use the value at the start of the page or at the end of the page? To address this question, we introduce optional @counter and @string declaration blocks and a new property, 'page-policy', that applies only to strings and counters. The following example places the chapter name in the header, specifying that it is the value of the string at the end of the page:

@string chapter { page-policy: last; }
@page { size:               21.0cm 29.7cm; /* A4 */
        @page-top { text-align: right;
                    vertical-align: center;
                    content: string (chapter);
        }
}

To use the chapter name as it was when the processing of the page started, the designer would specify a 'page-policy' of 'start' instead of 'last'. Designers can also use the value of a string or counter after its first state change on a page by specifying 'first'.

Named strings are a convenient way to pull metadata out of the document for insertion into headers and footers. In HTML, for example, META elements contained in the document HEAD can set the value of named strings. In conjunction with attribute selectors, this can be a powerful mechanism:

META[author] {string-set: author attr(author);}
HEAD > TITLE {string-set: title content();}
@page:left {
      @page-top {text-align: left;
                      vertical-align: middle;
                      content: string(title);}
}
@page:right {
      @page-top {text-align: right;
                       vertical-align: middle;
                       content: string(author);}
}

3.2 Page-based Counters

CSS2 counters apply to all elements. In CSS3, counters also apply to page contexts defined by means of the '@page' rule. This is useful for maintaining a page count, or for resetting figure or footnote counters at the beginning of each page. The following rules result in the placement of the current page number in the middle of the outside margin of each page:

@page {margin: 10%;
       counter-increment: page;
       counter-reset: footnote;
       @page-outside {text-align: center;
                      vertical-align: center;
                      font-family: Helvetica, Swiss, Arial, sans-serif;
                      font-weight: bold;
                      font-size: 2em;
                      content: counter(page);
       }
}

3.3 New 'content' Functions

As mentioned earlier, CSS2 introduced the 'content' property for introducing generated content into a styled document. Traditional headers and footers require the addition of several built-in functions for generated content. These functions apply to the new 'string-set' property as well.

[Here is where we can add additional functions for whatever HTTP header information we want to be able to access.]

3.3.1 content()

As demonstrated in the 'named strings' section, the 'content()' function extracts the textual content of the element in question. Sub-element text is included, but of course any document structure contained in the sub-tree is lost. In other words, the content is flattened.

3.3.2 date()

The 'date()' function returns the current date and/or time, formatted according to the specified formatting string. The formatting string uses the same format as that used in standard Web Server server-side-includes (which is standard locale-specific Unix (Posix?) functionality). [Anyone have a better idea? Consult HTML 4.0. Consult I18N group. We could make this a two argument function in order to support different kinds of dates, like current date, expires date, etc.]

3.3.3 uri()

The 'uri()' function returns the URI of the current document. For local files, this may simply be the local file name.

3.3.4 pages()

The 'pages()' function returns the total number of the pages in the document. This allows text such as "Page 1 of 5" to be inserted in margin boxes. This function is potentially expensive in terms of processing required, and only applies to paged media.

3.4 Strings, Counters and 'display:none'

In CSS2, elements which both increment counters and have the 'display:none' property value do not actually increment the counter. Since named strings are the textual equivalent of counters, we would expect named strings to exhibit the same behavior. However, there are times when authors might want to supress the display of an element in the document itself but include the element text in a header or footer.

Once example is a confidentiality (or intellectual property) clause. Let's say the author uses different style sheets depending on the capability of the user agent. There may be policy reasons that this clause must be displayed as part of the document. Therefore, the element resides in the main body of the document (so that it will always be displayed by older user agents). However, with a CSS3 compliant user agent the author might want to display this clause in the footer of each page when printed. In this case, there would be no reason to include redundant text in the document body. This author would want to set "display:none" on the element, but have the element set the value of a named string as follows:

.confidential {display: none;
               string-set: confidential content();}
@page {@page-bottom {text-align: center;
                     vertical-align: bottom;
                     text-weight: bold;
                     content: string (confidential);
       }
}

[Perhaps a better alternative would be to set 'content:' to an empty string if we end up extending 'content:' to apply to all elements. This would avoid interactions with 'display:none'. See "String and Counter properties" for a property that would work for this if we do not choose to extend 'content:'.]


4. Footnotes and Endnotes

This section intentionally left blank


5. Cross-references

In printed document, cross-references often include the page number of the target. Here is one example:

For more information, see chapter 9 (page 41).

When authoring structured documents it isn't known on what page the target will end up being printed on. Instead, the author inserts logical links between document elements. When the document is printed, page numbers can be determined and inserted in the right places.

Page numbers are only one example of how cross-references are presented in printed documents, and this section contains more examples. Given this markup:

   <P>For more information, see <A HREF="#overview">the overview</A>.

   <H1 ID="overview">Overview</H1>

it should be possible to generate this output:

  1. For more information, see the overview.
  2. For more information, see Overview.
  3. For more information, see page 41.
  4. For more information, see the overview (p41).
  5. For more information, see Overview (page 41).
  6. For more information, see chapter 9 (page 41).
  7. For more information, see chapter 9 (Overview) on page 41.

CSS3 accomplishes these presentations through:

Here are the counters needed to achive the presentations above:

   @page {
      counter-increment: page;
   }
   H1 { 
     counter-increment: chapter;
   }
   H1:before {
     content: counter(chapter);
   }

And here are the stylistic rules on the A element which will achieve the above presentations:

  1. no rules necessary
  2. A[HREF] { content: target-content(HREF) }
  3. A[HREF] { content: "page " target-counter(HREF, page) }
  4. A[HREF]:after { content: " (p" target-counter(HREF, page) ")" }
  5. A[HREF] { content: target-content "(page " target-counter(HREF, page) }
  6. A[HREF] { content: " chapter " target-counter(HREF, chapter) " (page " target-counter(HREF, page) ")" }
  7. A[HREF] { content: " chapter " target-counter(HREF, chapter) "(" target-content(HREF) ") on page " target-counter(HREF, page) }

target-content

The 'target-content(X)' function returns as a string the textual content of the URI contained in attribute X for the subject of the selector. If the subject of the selector doesn't have an attribute X, an empty string is returned. The case-sensitivity of attribute names depends on the document language.

target-counter

The 'target-counter(X, C)' function returns as a string the value of the counter C for the element pointed to by the URI contained in attrbute X for the subject of the selector. If the subject of the selector doesn't have an attribute X, or if the element pointed to by X doesn't have a value for the counter C, an empty string is returned. The case-sensitivity of attribute names depends on the document language.


6. Duplex layout

CSS3 introduces two new pseudo-properties for duplex printing: 'inside' and 'outside'. These values are appropriately mapped by the user agent to 'left' or 'right' on a page by page basis. In simplex printing situations, 'inside' maps to 'left' and 'outside' maps to 'right'. The 'inside' and 'outside' pseudo-properties can be used in most places where you can use 'left' and 'right'. This includes: padding, borders, margins, box offsets, clear, [clip rectangle?], background-position, text-align and caption-side.

These new pseudo-properties can greatly simplify the construction of headers and footers. The following rule results in the generation of the chapter name on the outside edge of the header area:

@page {margin: 10%;
       @page-top {margin-outside: 5%;
                  text-align: outside;
                  vertical-align: middle;
                  font-family: Times New Roman, Times Roman, Times, serif;
                  font-size: 85%;
                  content: string(chapter);}
}

Accomplishing the same thing without the new pseudo-properties would require two separate rules:

@page:left {
      @page-top {margin-left: 5%;
                 text-align: left;
                 vertical-align: middle;
                 font-family: Times New Roman, Times Roman, Times, serif;
                 font-size: 85%;
                 content: string(chapter);}
}
@page:right {
      @page-top: {margin-right: 5%;
                 text-align: right;
                 vertical-align: middle;
                 font-family: Times New Roman, Times Roman, Times, serif;
                 font-size: 85%;
                 content: string(chapter);}
}

The new pseudo-properties can also be used for 'mirrored' layout effects. The following stylesheet could be used to display a document using a three-column layout:

.notes {width: 25%;
        float: inside;
        margin: 1em;}
.body {width: 50%;
       margin: 1em;}
.links {width: 25%;
        float: outside;
        margin: 1em;}
Diagram [to be replaced with real diagram]
+------------------------------+------------------------------+
|                              |                              |
| +------+------------+------+ | +------+------------+------+ |
| |      |            |      | | |      |            |      | |
| |   L  |      B     |  N   | | |   N  |     B      |  L   | |
| |   i  |      o     |  o   | | |   o  |     o      |  i   | |
| |   n  |      d     |  t   | | |   t  |     d      |  n   | |
| |   k  |      y     |  e   | | |   e  |     y      |  k   | |
| |   s  |            |  s   | | |   s  |            |  s   | |
| |      |      T     |      | | |      |     T      |      | |
| |      |      e     |      | | |      |     e      |      | |
| |      |      x     |      | | |      |     x      |      | |
| |      |      t     |      | | |      |     t      |      | |
| |      |            |      | | |      |            |      | |
| +------+------------+------+ | +------+------------+------+ |
|                              |                              |
+------------------------------+------------------------------+
          Left Pages                     Right Pages

Perhaps the designer wants a vertical rule in between the body text and the links. We could do this with another thin, floated image, but in many cases it would be more convenient to use a border on either the body text block or the links block.

.links {width: 25%;
        float: outside;
        margin: 1em;
        padding-inside: 1em;
        border-inside: 1px solid blue;

[How far do we want to take this? To do background-position right, percentages should work with inside and outside as well. To do this, we'd need to be able to specify an origin for percentages. Right now it is fixed at the top-left (which is perhaps a bit Eurocentric anyway). What other percentage values ought to work with 'inside' and 'outside'?]

Note: The ':left' and ':right' page pseudo-classes are still useful for setting up page margins that are only used in duplex printing.


7. Property definitions

7.1 At Rules

7.1.1 Margin Boxes

The following at-rules are legal only within @page blocks.

Name:  @page-top-1
Alternate Name:  @page-top
Applies to:  @page rules

'@page-top-1' or '@page-top' instantiates a margin box at the top of the page. If more than one '@page-top-1' or '@page-top' rule are declared, the following rule overrides the previous rule(s).

Names:  @page-top-2, @page-top-3
Applies to:  @page rules

'@page-top-2' and '@page-top-3' instantiate additional margin boxes at the top of the page. These are typically used when the designer wants more than one field in page headers, each independently aligned. It is legal to have an '@page-top-2' or '@page-top-3' rule with no '@page-top-1' or '@page-top' rule. The three margin boxes are completely independent.

Name:  @page-right-1
Alternate Name:  @page-right
Applies to:  @page rules

'@page-right-1' or '@page-right' instantiates a margin box on the right side of the page. If more than one '@page-right-1' or '@page-right' rule are declared, the following rule overrides the previous rule(s).

Names:  @page-right-2, @page-right-3
Applies to:  @page rules

'@page-right-2' and '@page-right-3' instantiate additional margin boxes on the right side of the page. It is legal to have an '@page-right-2' or '@page-right-3' rule with no '@page-right-1' or '@page-right' rule. The three margin boxes are completely independent.

Name:  @page-bottom-1
Alternate Name:  @page-bottom
Applies to:  @page rules

'@page-bottom-1' or '@page-bottom' instantiates a margin box at the bottom of the page. If more than one '@page-bottom-1' or '@page-bottom' rule are declared, the following rule overrides the previous rule(s).

Names:  @page-bottom-2, @page-bottom-3
Applies to:  @page rules

'@page-bottom-2' and '@page-bottom-3' instantiate additional margin boxes at the bottom of the page. These are typically used when the designer wants more than one field in page footers, each independently aligned. It is legal to have an '@page-bottom-2' or '@page-bottom-3' rule with no '@page-bottom-1' or '@page-bottom' rule. The three margin boxes are completely independent.

Name:  @page-left-1
Alternate Name:  @page-left
Applies to:  @page rules

'@page-left-1' or '@page-left' instantiates a margin box on the left side of the page. If more than one '@page-left-1' or '@page-left' rule are declared, the following rule overrides the previous rule(s).

Names:  @page-left-2, @page-left-3
Applies to:  @page rules

'@page-left-2' and '@page-left-3' instantiate additional margin boxes on the left side of the page. It is legal to have an '@page-left-2' or '@page-left-3' rule with no '@page-left-1' or '@page-left' rule. The three margin boxes are completely independent.

The following CSS properties are allowed inside of margin box at-rules:

7.1.2 Strings and Counters

CSS3 introduces properties which apply directly to counters and named strings. Here we introduce string and counter declaration blocks, where non-default property values can be set. See String and Counter Properties for the actual property definitions. Note: Named strings and counters do not require declaration before use.

Name:  @counter <identifier> {}

The @counter rule declares a named counter. The following CSS properties can be specified within the counter declaration block:

Note: The 'list-style-type' specified in the two-argument form of the 'counter()' function overrides a 'list-style-type' specified in the declaration block.

Name:  @string <identifier> {}

The @string rule declares a named string. The following CSS properties can be specified within the string declaration block:

7.2 Element Properties

Property:  string-set
Value:  <identifier> [ <string> | <uri> | <counter> | attr(X) | content() | date(X) | uri() | pages() | open-quote | close-quote | no-open-quote | no-close-quote ]+ | none | inherit
Initial:  none
Inherited:  no
Applies to:  all elements
Percentages:  N/A
Media:  All

7.3 String and Counter Properties

These properties are allowed only inside of an @string or @counter declaration.

Property:  page-policy
Value:  <start> | <first> | <last>
Initial:  start
Inherited:  N/A
Applies to:  counter and string declarations
Percentages:  N/A
Media:  Paged

[I believe we can avoid creation of the following property if we extend 'content:' to all elements. Then, instead of setting "display:none" to hide an element, we could set 'content' to an empty string. I think it is a Good Thing to avoid interactions with display properties.]

Property:  hidden-policy
Value:  <ignore> | <normal>
Initial:  ignore
Inherited:  N/A
Applies to:  counter and string declarations
Percentages:  N/A
Media:  All

7.4 Pseudo-properties

[Inside/Outside descriptions go here.]