Importing a CSS stylesheet

TopLeaf provides a means of initializing the style for a publication by interpreting a CSS (Cascading Style Sheet) file. It is important to note that TopLeaf and CSS use different models for describing style, so it is not possible to do a full conversion. Instead, the mappings created represent a starting point from which more complete mappings can be developed.

The importer attempts to recover from syntax errors in the CSS file, as required by the CSS standard (http://www.w3.org/TR/REC-CSS2) but using the importer on invalid files may lead to unpredictable results. If a rule either cannot be recognised, or cannot be expressed as a TopLeaf mapping, it is silently ignored.

The importer only uses information from the CSS file, not the DTD. This means that mappings may be created that refer to elements and/or attributes that are not in the DTD.

The importer can be run when a publication is created or by selecting Import mappings in the Mapping Editor. If any errors are encountered a dialog box will open with details of the problem.

Selectors

Only rules whose selectors can be converted into the format used by TopLeaf mappings will be read — all other rules are ignored. TopLeaf mappings can refer to any number of elements, using parent and ancestor relationships. Attribute selectors are only allowed on the lowest element in the hierarchy.

The following are examples of selectors that will not be processed:

  • * [lang=fr] — must map a specific element

  • head1 + head2 — TopLeaf mappings are not context sensitive

  • chapter * title — generic ancestor syntax not supported

  • head1.intro — class syntax not supported

  • head1#chap3 — ID syntax not supported

  • head1[title] — attribute value must be specified

  • head1[class=intro][style=stressed] — multiple attribute values not supported

  • head1[class=intro]/title — attribute selectors allowed only on element being mapped.

Note that TopLeaf generally provides alternate means of achieving the above functions. For example a CSS head1[title] effect could be achieved by the plain head1 mapping in TopLeaf if a head1[title="#IMPLIED"] mapping is defined to handle tags where the attribute does not exist.

The importer recognizes the selector $document (an XMetaL extension) for setting the document mapping. Note that this is not the same as the universal selector (*).

Cascading

CSS implements a cascade that allows several rules to apply to a particular element, with the more specific rules overriding the less specific ones. TopLeaf, on the other hand, only applies a single mapping to each element, which leads to a much simpler and easier to manage system.

The importer does a limited amount of processing to simulate the effect of cascading. For example, in the following two rules:

title {
    display: block;
    color: red;
}

chapter>title {
    font-weight: bold;
    color: green;
}

the information from the first rule is inherited by the second, except where there is a conflict. The end result is as if the two rules were specified as follows:

title {
    display: block;
    color: red;
}

chapter>title {
    font-weight: bold;
    color: green;
    display: block;
}

Importing other CSS stylesheets

An @import rule that specifies a stylesheet with a relative URI and does not specify any media is processed by including the text of the Stylesheet at that point. Note that this method is not what is described in the specification, but will give the same result in most cases.

The standard says that rules in the source stylesheet take precedence over rules in an imported stylesheet. Since the importer uses the last rule it sees for a given selector, the same effect is achieved.

Note that the importer ignores !important for determining rule precedence.

Font names

Fonts are handled very differently in CSS and TopLeaf. Since CSS is designed to facilitate delivery to multiple media, it allows for the specification of a list of fonts from which the renderer chooses the most appropriate. TopLeaf, on the other hand, is capable of much more precise formatting which requires a specific font to be selected.

For any given mapping, the importer either selects a specific font, or leaves it to be inherited from the ancestor elements. The following table shows how fonts are selected. Each string in the font-family list is compared in turn against the values in the first column (ignoring case). If the string starts with the value in the first column, then the font name in the second column is used. The first successful match determines the font. If there are no matches, the mapping inherits the font name from its ancestors.

Match String Font Used
serif Times New Roman
sans Arial
monospace Courier New
times Times New Roman
arial Arial
courier Courier New

Generated text

Text created using the :before and :after pseudo-elements is generated in TopLeaf using the pre- and post-content properties of the mapping. If the rule that generates the text contains only a content property, the text is inserted directly into the content box of the appropriate mapping. If the rule contains other properties that require mapping (for example, display: block;) then a custom marker is created for the generated content. For example, a rule with selector procedure>step:before could generate a custom marker called %Before_ProcedureStep.

The converter attempts to convert Unicode escapes (hexadecimal digits following a \) in generated content into XML character references. Note that the CSS2 specification requires that whitespace following a Unicode escape is part of the character unless there are exactly 6 hexadecimal digits. Some CSS processors incorrectly include the terminating whitespace as part of the content.

The following conversions of generated content take place:

  • runs of whitespace are replaced by the <space breakable="yes"/> directive

  • a line break character (for example, “\A”) is replaced by the <break/> directive

  • a non-breaking space character (for example, “\A0”) is replaced by the <space breakable="no"/> directive

  • an attribute function (for example, “attr(style)”) is replaced by an attribute reference (e.g. {@style})

  • limited support for the “counter()” function is implemented (see below)

Counters are supported in generated text, but only with “decimal” style. The counter-reset and counter-increment properties are recognized.

Nested counters are simulated by using the <stack/> directive to implement the counter-reset property. This will yield the same result as CSS when each element that emits a counter has a reset property in its parent.

Recognized properties

The properties recognized by the importer are:

  • display

  • font-family (but see “Font names”)

  • font-size

  • font-style (italic only)

  • font-weight (bold only)

  • text-decoration (underline only)

  • margin-top

  • margin-bottom

  • margin-left

  • margin-right

  • text-align (left, right, center or justify only)

  • color

  • border (solid only)

  • border-style (solid only)

  • list-style-type (only if the rule has “display: list-item”)

  • white-space (pre only, if display is block)

  • page-break-before (always or avoid only)

  • page-break-after (always or avoid only)

  • page-break-inside (avoid only)

  • counter-reset

  • counter-increment

For properties that define a measurement, the property will only be recognized if it defines an absolute value. Measurements with a unit of em or ex are ignored. A unit of px (pixels) is treated the same as pt (points).