[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Runnig heads. (long) XSLT strikes back.


Subject: Runnig heads. (long) XSLT strikes back.
From: Paul Tchistopolskii <paul@xxxxxxx>
Date: Thu, 14 Oct 1999 01:42:02 -0700


0. FOREWORD

This proposal is a followup to that of James Tauber, contained
in his letter to XSL-list of Mon, 11 Oct 1999. I refer to it
throughout the whole text; see the attached original message
(sorry for inevitable overquoting). The corresponding quotes
will be marked JT>. An important observation came from Jeremy
H. Griffith in his message to XSL-list of Mon, 11 Oct 1999; thanks!

There were a lot of discussion about running headers, dictionary-style
headers, etc; two main claims seem to be universally accepted by the
participators (correct me if I'm wrong):

1) Running headers is a kind of "basic" functionality, and
   it's amazing that nothing is proposed in the WD; they should
   be creatable by some simple mechanism - not much more complicated
   than using a default \section in LaTeX :-).

2) In many cases, there can hardly exist simple constructs to
   describe the contents of the headers (an example by Sebastian
   Rahtz of suppressing headers on pages occupied by floats,
   or Paul Tchistopolski's example of page numbering in two-page
   layout).

Considering this, we see a need for *two different* improvements
in the XSL WD:

- first is a simple way of doing running headers/footers;
- second is a support for *rendering-time scripting*.

Naturally enough, if we can invent something powerful and simple
in the same time that will satisfy both requirements, we've got
the stake; but introducing separate, non-scripting techniques
for running headers seems also plausible.

A proposal by James contains basic elements for both things:
there, <fo:static-content> functionality is extended to allow
for more precise flow mapping, and basic scripting (running fields)
are envisaged.

Upon some reflection, we have come to the following idea of
possible improvements to James' work:

- first, we try to extend <fo:static-contents> functionality
   so that creating simple running headers won't require using
   any running-fieds at all;
- second, we try to extend <jt:running-fields> functionality to allow
   for more complicated data transformations (like in Sebastian's
   and Paul's examples).

The two things are independent; we can implement either of them,
or both. In our opinion, implementing the first one would be
relatively easy and will enhance the functionality of FOs
considerably, requiring only minor changes in the element set;
this is yet another argument for keep them distinct.

1. RUNNING HEADERS/FOOTERS

(In what follows, I will use a 'foext:' namespace prefix to mark
proposed extensions to FOs. I substitute them for James' 'jt:', too.)

James wrote:
JT> My proposal for that is for static-content to have two additional
JT> properties: region and master.
JT>
JT> <fo:static-content jt:region="before" jt:master="right">...</fo:static>
JT>
JT> means that this is the static content to put in the before region on
pages
JT> using the "right" master.

<aside>
  It looks like the jt:region is sort of redundant; we have
  flow-name="xsl-before" to express exactly the same thing.
</aside>

Including a master name is an excellent idea! In this way, you can
specify different static-contents for different page instances
inside the same page-sequence. For example, this would be a way
of putting page numbers to the outside edge of the header:

  <fo:page-sequence>
    <fo:static-content flow-name="xsl-before"
                       foext:page-master="odd-pages">
      <fo:block text-align="end"><fo:page-number/></fo:block>
    </fo:static-content>
    <fo:static-content flow-name="xsl-before"
                       foext:page-master="even-pages">
      <fo:block text-align="start"><fo:page-number/></fo:block>
    </fo:static-content>

    <fo:flow flow-name="xsl-body"> ... </fo:flow>

  <fo:page-sequence>

And immediately, another idea came to me: what about permitting
even more instances of <fo:static-content> *inside the <fo:flow>*?

For instance, we can enable placing <fo:static-content> in the
beginning of a <fo:display-sequence>:

  <!ELEMENT fo:display-sequence
      (fo:static-content*, (%block-level;)+ )>

This will be interpreted as follows: for each side region on
a given page, we collect all instances of fo:static-content
from all display-sequences present on the page that match the
pagemaster name (including the default one specified on the
<fo:page-sequence>), and then choose the right instance according
to some criteria, described further.

(Obviously, we can also designate some other, freshly new element,
for such a scope; but, in our opinion, fo:display-sequence has
suitable semantics and very few real uses in the current XSL WD;
recycling it won't be bad ;-)).

1.1. Choosing the right <fo:static-content>
To control the choice, we feel two kinds of attributes are necessary:

- a 'priority' attribute on the static-content; this would be
  a regular numeric priority.

- to control the choice if there are more that one instance
  having the same priority, we need something like a CSS3
  'page-policy' attribute (see http://www.w3.org/TR/css3-page,
  3.1 "Named strings"). Let us call it this same name -
  foext:page-policy - until we find something better (I'm not
  a native speaker of English, so I will gladly let someone
  else choose the right mnemonics). The values are the same
  as in the CSS3, proposed also by Jeremy H.Griffith as possible
  values for James' 'which' attribute:

    - start - choose the one occurred on the page before and
              continued on this page; if no such instance
              exists, choose the 'first' as described below;

    - first - the first one that starts on this page;

    - last  - the last one that occurs on this page (and possibly
              goes to the next page).

   This property will be used by fo:static-content, but I am
   tempted to make it inheritable (and specify it on <fo:flow>).
   The default value can be made "start" or "first" for the
   before-edge and start-edge, and "last" for after-edge and
   end-edge.

- in the extreme case where there are more than one instance
  with the same priority at the same place (this can easily
  happen with page-policy="start"), choose the inner one.

- having two or more static-contents that match the same region
  in one display-sequence is considered an error, and punished
  by neglecting all but the first.

1.2. An example of how all this will behave.

A. A chapter number is displayed in the header; if a page
break occurs inside a chapter, the header on the next page
will get the words "continued", and the footer on the
previous page will get the words "continued on next page".

<fo:flow flow-name="xsl-body">

  <fo:display-sequence>  <!-- outer display-sequence -->
    <fo:static-content flow-name="xsl-before"
                       foext:page-policy="first">
      <fo:block>Chapter 1</fo:block>
    </fo:static-content>

    <fo:display-sequence> <!-- inner display-sequence -->
      <fo:static-content flow-name="xsl-before"
                         foext:page-policy="start">
        <fo:block>Chapter 1</fo:block>
      </fo:static-content>

      <fo:static-content flow-name="xsl-after">
        <fo:block>continued on next page</fo:block>
      </fo:static-content>

      ........... <!-- the contents of Chapter 1 -->

    </fo:display-sequence>
  </fo:display-sequence>

  ............ <!-- same structures for other Chapters -->

</fo:flow>

If a page break occurs between the pages, active static-contents
are those from the outer display-sequence. If, instead, the page
break split the chapter, the inner headers/footers will be
applicable. Note that I have not specified the page-policy for
the footer, relying on the default behaviour; you can also
add a 'foext:page-policy="last"' there.


B. The header contains a chapter title. If a table is present
on the page, its caption is added to the header.

<fo:flow flow-name="xsl-body">

  <fo:display-sequence>  <!-- chapter -->
    <fo:static-content flow-name="xsl-before"
                       foext:page-policy="first"
                       foext:priority="1">
      <fo:block>Chapter 1</fo:block>
    </fo:static-content>

    ..... <!-- the contents of Chapter 1 -->

    <fo:display-sequence> <!-- table -->
      <fo:static-content flow-name="xsl-before"
                         foext:page-policy="first"
                         foext:priority="2">
        <fo:block>Chapter 1, Table 1</fo:block>
      </fo:static-content>

      <fo:table-and-caption>
         <fo:table-caption>
            <fo:block>Table 1</fo:block>
         </fo:table-caption>
         <fo:table>
           ........... <!-- the contents of Table 1 -->
         </fo:table>
      </fo:table-and-caption>
    </fo:display-sequence>

    ..... <!-- the contents of Chapter 1 cont'd -->

  </fo:display-sequence>

  ...... <!-- other Chapters -->

</fo:flow>

In this example, a higher priority of the table-related display-sequence
ensures that the correspondent header will be chosen every time a
table occurs on the page.

2. RENDERING-TIME SCRIPTING

Let us view the problem from a broader perspective: what do we
need in order to calculate contents and presentation effects,
depending on the actual page layout? Two things are essential:

- to determine the current state on the formatter;
- to convert the results of the query into some content.

2.1. As for the state of the formatter, the task is achieved by setting
up some system of reference points inside the document. The simplest
positioning system is contained in the CSS3 (op.cit.): you can link
named string sets to elements, and retrieve values by the string name,
selecting the first or the last string on the page.

James Tauber proposes a similar mechanism:

JT> The basic idea is that a section header, dictionary head word,
JT> or whatever, sets a field and the static-content can "retrieve"
JT> the first or last value set for that field on that page.

JT> To set a field one uses a "running-field-set" element with the name of
the
JT> field in the "name" attribute and the value of the field the contents of
the
JT> element.

JT> To retrieve a field one uses a "running-field" empty element with the
name
JT> of the field in the "name" attribute and another attribute "which" which
JT> takes the value "first" or "last" which indicates which value of the
field
JT> on that page to take.


In this way, you can link fragments of a formatting tree, and not
merely plain strings, to named strings. The system of references,
however, remains linear: no hierarchy of jt:running-field-sets is
envisaged.

To my mind, a further step should consist in explicitly
specifying the *visibility scope* of every running-field-set,
making the whole structure *tree-like*, rather than linear.
For this scope, we may further extend the semantics of the
<fo:display-sequence>, making it a universal scope delimiter:

  <!ELEMENT fo:display-sequence (
       foext:running-field-set*,
       fo:static-content*,
       (%block-level;)+
  )>

It is evident that, in this way, we can pass as much information
about the document contents as we need. To structure the whole
thing better, we suggest adding a special attrbute to
fo:display-sequence - let's call it "foext:class" - to group
display-sequences that denote content chunks of the same level.

For instance,

  <fo:display-sequnce foext:class="my_chapter">
     ....
  </fo:display-sequence>

would embrace all the contents of a single chapter in a book,
and thus serve as a natural scope delimiter for all static-contents
and running-field-sets pertaining to this chapter.

2.2. Once we have set up a tree-based system of reference points
in the document, it is absolutely natural to use a subset of
XSLT/XPath for rendering-time scripting. (This is also natural
from the architectural point of view, as XSL and XSLT are supposed
to be closely related). We suggest the following mapping of XSLT
concepts:

   <fo:display-sequence> will correspond to node element;
   <foext:running-field-set> will correspond to attribute.

These elements may be used in expressions that resemble those
of the XSLT. For instance, to create a dictionary-style header
as in the James' example, we do the following:

<fo:page-sequence>
  .....
  <fo:static-content flow-name="xsl-before">
    <fo:block>
      <foxsl:value-of select="entry[first()]/@headword"/> &emdash;
      <foxsl:value-of select="entry[last()]/@headword"/>
    </fo:block>
  </fo:static-content>

  <fo:flow flow-name="xsl-before">
    <fo:display-sequence>
      <fo:running-field-set name="headword">example</fo:running-field-set>
      <fo:block>
         Example, n. Fact, thing, illustrating general rule.
      </fo:block>
    </fo:display-sequence>

    <!-- more entries -->

  </fo:flow>
</fo:page-sequence>

Note that in this case, James' <jt:running-field> is replaced
by a more powerful scripting mechanism. I have temporarily
assigned a namespace of 'foxsl' to it. Having this, you get
control on a very subtle presentation details. For instance,
to include all words on a page into the header, you modify
the static-content in the above example to the following:

  <fo:static-content flow-name="xsl-before">
    <fo:block>
      <foxsl:value-of select="entry[first()]/@headword"/>
      <foxsl:for-each select= "entry[position()>first()]">
        &emdash; <foxsl:value-of select="./@headword"/>
      </foxsl:for-each>
    </fo:block>
  </fo:static-content>

(I foresee that it will be difficult to separate rendering-time
scripting from the transformation part of the stylesheet itself,
and people will get confused. The only excuse is that, with
running headers implemented as above, the scripting will be
required only in very specific points, like the above examples).

(The whole paragraph 2.2. is very indicative. I'm not sure
that there are no hidden problems with this. However, the idea
of using XSLT for all kinds of XSL-related processing looks
appealing in general).

<obsolete>
Comments are welcome.
</obsolete>


----- Original Message -----
From: James Tauber <jtauber@xxxxxxxxxxx>
To: <xsl-list@xxxxxxxxxxxxxxxx>
Sent: Sunday, October 10, 1999 10:38 PM
Subject: Re: XSL FO how to make things go. Re: Q: XML+XSL transforms to
aprint-ready
format


> > I'll send my proposal in a separate message.
>
> Okay. Here is my proposal. There are problems with it, but I think it is a
> start.
>
> First of all, we need a way of specifying static-content on a per-master
> basis as well as a per-region basis so that, for example, odd and even
pages
> can differ in header.
>
> My proposal for that is for static-content to have two additional
> properties: region and master.
>
> <fo:static-content jt:region="before" jt:master="right">...</fo:static>
>
> means that this is the static content to put in the before region on pages
> using the "right" master.
>
> Next we need two new FOs in order to achieve running headers (including
> dictionary-style). One, which produces area(s), occurs in static-content
(eg
> the header), the other occurs in the body. The basic idea is that a
section
> header, dictionary head word, or whatever, sets a field and the
> static-content can "retrieve" the first or last value set for that field
on
> that page. In the absence of a field setting on a page, the last value set
> for the previous page in the
> page-sequence would be used, ultimately defaulting to an empty string.
>
> NOTE: The names of the FOs and their properties are just what occur to me.
I
> have no problem changing them.
>
> To set a field one uses a "running-field-set" element with the name of the
> field in the "name" attribute and the value of the field the contents of
the
> element.
>
> To retrieve a field one uses a "running-field" empty element with the name
> of the field in the "name" attribute and another attribute "which" which
> takes the value "first" or "last" which indicates which value of the field
> on that page to take.
>
> So, a section might result in a FO tree fragments like:
>
> <jt:running-field-set name="section">3.4 Formatting
> Model</jt:running-field-set>
> <fo:block space-before.optimum="2cm" space-after.optimum="1cm"
> font-size="24pt">3.4 Formatting Model</fo:block>
> <fo:block>In XSL, one creates a number of formatting objects...</fo:block>
>
> The static-content might then look like:
>
> <fo:static-content region="before" master="right"><fo:block
font-size="10pt"
> font-style="italic" text-align-last="right"><fo:running-field
name="section"
> which="last"/></fo:block></fo:static-content>
>
> In a dictionary, the body might have fragments like:
>
> <fo:running-field-set name="headword">example</fo:running-field-set>
> <fo:block><fo:inline-sequence
> font-weight="bold">example</fo:inline-sequence>, n. Fact, thing,
> illustrating general rule</fo:block>
>
> and then have static-content
>
> <fo:static-content region="before" master="right"><fo:block
font-size="10pt"
> font-style="italic" text-align-last="right"><fo:running-field
> name="headword" which="first"/>&emdash;<fo:running-field name="headword"
> which="last"/></fo:block></fo:static-content>
>
> As I said at the start, there are problems with this. A more general
> solution is needed and I hope it will be in XSL eventually.
>
> James
>





 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



Current Thread
Keywords