Les formatting-objects (XSL-FO)

Jean-François Perrot

Révision du 7 mars 2012

  1. Principe
    1. Idée
    2. Organisation générale
      1. Un élément unique <layout-master-set>
      2. Une suite d'un ou plusieurs éléments <page-sequence>
    3. Théorie du <block> et de l'<inline>
    4. Mise en œuvre avec Apache-Fop-1.0
      1. Emploi du script fop :
      2. Mise en œuvre de la classe Fop

  2. Premier exemple
    1. Organisation du fichier FO
    2. Le fichier lui-même
    3. Le fichier pdf produit

  3. Production d'un fichier XML-FO
    1. XSLT
    2. Mise en œuvre avec Apache-Fop 1.0
    3. Deuxième exemple
      1. XML
      2. FO

  4. Deuxième vague
    1. Séquences à plusieurs types de pages (chapitres, actes, etc)
      1. L'élément page-sequence-master
      2. Chaque élément fo:page-sequence
      3. L'exemple des actes dans les pièces de Shakespeare
    2. Réglages fins : polices, tailles, espaces...


  1. Principe

    1. Idée

      (voir Wikipédia)
      Il s'agit d'un moyen d'exprimer les indications de mise en forme d'un texte
      de manière suffisamment précise pour pouvoir piloter un générateur de pdf.

      Par rapport à l'expérience usuelle des fichiers-texte (y compris HTML), la nouveauté est la prégnance de la page.
      Par exemple, un fichier HTML interprété par un navigateur se déroule continûment à travers l'écran, sans pagination
      (ce qui rend aléatoire l'effet produit par l'ordre d'impression (Print) d'un tel document !).
      Il s'agit désormais de spécifier les modalités de la mise en pages du texte.
      Non de composer le texte page par page comme faisaient autrefois les typographes,
      mais de définir précisément le processus qui va produire des pages à partir d'un texte non paginé.

      Pour comprendre ce formalisme assez verbeux, il faut se placer dans la perspective de la production finement ajustée d'un document complexe,
      comme un livre, avec plusieurs types de pages (couverture, page de garde, têtes de chapitres, pages "normales", etc),
      chaque page ayant ses marges, éventuellement un en-tête et un pied de page.
      Du même coup, pour être illustratif un exemple a besoin d'être un peu long...

      Pour une présentation à la fois lisible et approfondie, voir le chapitre 18 de la XML Bible, Second Edition.

    2. Organisation générale

      Un fichier FO est essentiellement composé de deux parties :
      1. La spécification des différents formats de pages utilisés (via un layout-master-set)
      2. La donnée du contenu qui va se déverser dans ces pages,
        sous la forme d'une ou plusieurs séquences de pages (page-sequence).

      L'élément-racine appelé <root> contient donc essentiellement :
      1. Un élément unique <layout-master-set>
        (que l'on peut considérer comme une méta-information, analogue au <head> de HTML).

        Il est formé
        • (obligatoirement)  d'un ou de plusieurs éléments <simple-page-master>
          dont chacun définit un format de page
        • et de (optionnellement), un ou plusieurs éléments <page-sequence-master> 
          dont chacun définit la structure d'une séquence de pages en termes de formats <simple-page-master>.

        Structure d'un <simple-page-master> :
        • il porte un nom (attribut master-name) pour mise en œuvre ultérieure
        • il fixe la taille des pages (dimensions & marges extérieures)
        • il définit l'organisation de la page en régions :
          • <region-before> = l'en-tête (facultative)
            dont la taille est fixée par un attribut extent
          • <region-after> = le pied de page (id.)
          • <region-body> = le corps de la page (obligatoire
            dont les attributs margin-top et margin-bottom doivent être supérieurs aux extent des régions correspondantes !
        • de manière facultative, il fixe le nombre de colonnes dans la page
        • et c'est tout !
          le contenu de l'en-tête et du pied de page éventuels, la numérotation des pages,
          tout les reste est renvoyé aux éléments <page-sequence>.

        Sur l'élément <page-sequence-master>, voir plus loin.
      2. Une suite d'un ou plusieurs éléments <page-sequence>
        Qu'il y en ait un ou plusieurs différencie la <page-sequence> de FO du <body> de HTML,
        mais à part cette multiplicité son rôle est le même : elle représente le contenu à imprimer sur autant de pages qu'il sera nécessaire.

        Dans le cas le plus simple, toutes ces pages seront du même modèle, décrit par l'un des éléments <simple-page-master>,
        qui est connu par son nom (master-name) valeur de l'attribut master-reference.
        Dans les cas plus compliqués (par exemple, pages de droite / pages de gauche), l'attribut master-reference désigne l'un des
        <page-sequence-master>, qui décrit la manière dont les pages de différents formats sont agencées dans la séquence.

        Il peut y avoir un nombre quelconque de <page-sequence>, chacun gérant la numérotation de ses pages.

        • Chaque <page-sequence> contient un seul fils <flow>
          qui enveloppe une séquence non bornée de <block>s, de <table>s, de listes etc.
          Son attribut flow-name désigne la zone de la page où son contenu va se déverser
          (le plus souvent la zone centrale "xsl-region-body").

          Répétons que le contenu du <flow> va se répartir en autant de pages qu'il sera nécessaire...
          Au contraire, le contenu des en-têtes et des pieds de page est souvent fixé lors de la définition de la <page-sequence>.

        • Chaque <page sequence> peut contenir deux fils <static-content> 
          l'un orienté vers l'en-tête (flow-name="xsl-region-before"), 
          l'autre vers le pied de page (flow-name="xsl-region-before").

        • L'un comme l'autre se définit comme une succession de <block>.

    3. Théorie du <block> et de l'<inline>

      L'élément fondamental du <flow> dans la <page sequence>  est le <block>.
      Donnons-en un aperçu :
      • il correspond grosso modo au paragraphe de Word, en ce sens qu'il commande une nouvelle ligne,
        avec peut-être indentation (attribut  text-indent ) ;
      • il porte les indications d'alignement, par l'attribut  text-align ;
      • ainsi que de nombreuses autres propriétés (telles que font-weight="bold" et font-style="italic"):
        on peut demander qu'il ne soit pas coupé par un saut de page intempestif (attribut  keep-together="always")
        [Mais attention !
        Tous ces desiderata peuvent fort bien s'avérer contradictoires !
        Nous sommes là dans l'art du typographe, pas dans une description formelle à la sémantique bien définie.
        À telle enseigne qu'il n'y a pas de grammaire officielle (schéma ou rng) pour FO.
        Il faut donc être prudent et attentif...
        Dans le cas de keep-together="always", par exemple, sa présence a pour effet d'inhiber le processus
        de coupure de lignes ! Il faut donc s'assurer que le texte visé tient dans l'espace prévu tel quel.]

      • et surtout, comme en HTML, un <block> peut contenir d'autres <block>s,
        qui héritent des propriétés de leur père-contenant.

      La notion de <block> s'oppose à celle d'<inline>, qui est homologue du <span> en HTML
      Comme son nom l'indique, l'<inline> ne provoque pas de saut de ligne !
      On l'utilise donc pour varier les indication de format le long d'une même ligne.
      C'est lui le spécialiste des propriétés font-weight="bold", font-style="italic", font-size="8pt", etc
      mais pas du text-align ni du space-after, qui sont le privilège du <block>.

      Exemple de bloc composé de 3 sous-blocs, dont le dernier contient un <inline> :

            <fo:block font-size="10pt" keep-together="always">
               <fo:block space-after="7pt">Marguerite et Marcel AMBRONIOT</fo:block>
               <fo:block space-after="5pt">Saint André</fo:block>
               <fo:block space-after="37pt">38 256 <fo:inline font-weight="bold">Vianney</fo:inline>
               </fo:block>
            </fo:block>

      qui donnera quelquechose comme Ex Typo

    4. Mise en œuvre avec Apache-Fop-0.1

      Ref :  http://xmlgraphics.apache.org/fop/

      FOP = Formatting Objects Processor.
      La version 1.0 (2010) est la plus récente au 7/03/2012.
      Sa mise en œuvre est décrite dans le Quick Start Guide. Voir les sections Run FOP  - intitulée Running Apache FOP - et
       Calling FOP from a Java Application - intitulée Apache FOP: Embedding How to Embed FOP in a Java application.

      Le logiciel Apache-Fop fournit une classe org.apache.fop.apps.Fop qui permet de produire un fichier pdf à partir d'un fichier XML-FO.
      Il propose aussi un script multi-options fop qui permet d'effectuer diverses opérations en ligne de commande.

      • Emploi du script fop :
        • fo --> pdf : sh fop-1.0/fop foo.fo foo.pdf
          ou bien : sh fop-1.0/fop -fo foo.fo -pdf foo.pdf

        • xml --> pdf (via une transformation XSLT donnée par foo.xsl qui engendre du fo, voir plus loin)
          sh fop-1.0/fop -xml foo.xml -xsl foo.xsl -pdf foo.pdf

      • Mise en œuvre de la classe Fop
        • Création à partir d'une instance de org.apache.fop.apps.FopFactory,
          en passant en paramètre
          1. une constante indiquant le type de document à produire (pdf, ps, ou autre)
          2. un OutputStream

          String fichOut = le nom du fichier pdf de sortie
          out = new BufferedOutputStream(new FileOutputStream(new File(fichOut)));
          Fop fop = FopFactory.newInstance().newFop(MimeConstants.MIME_PDF, out);


        • Le document XML-FO es toujours communiqué au processeur fop sous la forme d'une instance de
          javax.xml.transform.sax.SAXResult.

          Un tel objet est conforme à l'interface javax.xml.transform.Result.
          Il a pour fonction d'envoyer une séquence d'événements SAX à un handler capable de les interpréter.
          [Sur le principe de la technique SAX et sur sa mise en œuvre avec Java, voyez ici.]

          Ce handler doit être conforme à l'interface org.xml.sax.ContentHandler,
          et il doit être passé en paramètre lors de la création du SAXResult.
          Dans le cas qui nous intéresse ici, ce handler est fourni par le processeur fop lui-même,
          en invoquant  fop.getDefaultHandler().

          Result res = new SAXResult(fop.getDefaultHandler());

        • Enfin, l'opération est lancée sous la forme d'une transformation au sens de javax.xml.transform.
          Cette transformation produit l'objet res à partir du document XML-FO, et ce faisant active le processeur fop.
          Comme nous le verrons plus loin, elle peut aussi produire l'objet res à partir d'un document XML
          et d'un document XSLT qui construit le FO à partir du XML.

          Dans le cas où on dispose du document FO logé dans un fichier, les choses s'écrivent ainsi :

          String fichIn = le nom du fichier XML-FO
          Source src = new StreamSource(new File(fichIn));
          Transformer tf = TransformerFactory.newInstance().newTransformer();
          transformer.transform(src, res);
          out.close();


  2. Premier exemple

    Le fichier XML-FO que voici représente la mise en pages de la liste des adhérents d'une association hypothétique,
    avec leurs noms et adresses. Voici le fichier pdf correspondant.

    1. Organisation du fichier FO

      • On souhaite l'imprimer avec une page de couverture.
        Il y a donc deux  simple-page-master, nommés couverture et adhérents,
        et deux page-sequence qui le mettent en œuvre.

      • Dans les pages autres que la couverture, on veut
        • un en-tête rappelant le nom de l'association
        • un pied de page portant le numéro de page.
        La page-sequence "adhérents" contient donc deux static-content,
        orientés vers les region-before et region-after qui ont été dûment spécifiées dans le  simple-page-master,
        en plus de son flow, qui se déverse dans region-body,
        et qui va requérir plusieurs pages.

    2. Le fichier lui-même

      <?xml version="1.0" encoding="utf-8"?>
      <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

        <fo:layout-master-set>
           <fo:simple-page-master master-name="couverture"
              page-height="29.7cm"
              page-width="21cm"
              margin-top="1cm"
              margin-bottom="1cm"
              margin-left="1.5cm"
              margin-right="0.5cm">
              <fo:region-body margin-top="12cm" margin-bottom="10cm"/> <!-- obligatoire !!! -->
           </fo:simple-page-master>
          
           <fo:simple-page-master master-name="adhérents"
              page-height="29.7cm"
              page-width="21cm"
              margin-top="1cm"
              margin-bottom="1cm"
              margin-left="1.5cm"
              margin-right="0.5cm">
            <fo:region-body margin-top="2cm" margin-bottom="2cm"/>
            <!-- doivent être supérieures aux "extent" des régions correspondantes -->
            <fo:region-before extent="2cm"/>
            <fo:region-after extent="1.5cm"/>
           </fo:simple-page-master>
        </fo:layout-master-set>
       
        <fo:page-sequence master-reference="couverture">
            <fo:flow flow-name="xsl-region-body">
              <fo:block text-align="center" font-size="14pt" font-style="italic" space-after="16pt">
               Association des ocarinistes de Bagnolet
              </fo:block>
              <fo:block text-align="center" font-size="18pt" font-weight="bold">
               Liste des adhérents
              </fo:block>
            </fo:flow>
        </fo:page-sequence>
       
        <fo:page-sequence master-reference="
      adhérents ">
       
          <fo:static-content flow-name="xsl-region-before"> <!-- attention ! pas "flow" -->
            <fo:block text-align="center" font-size="8pt" font-style="italic">
            Ocarinistes de Bagnolet
            </fo:block>
          </fo:static-content>
         
          <fo:static-content flow-name="xsl-region-after"> <!-- idem -->
            <fo:block text-align="center">
            p. <fo:page-number />
            </fo:block>
          </fo:static-content>
         
          <fo:flow flow-name="xsl-region-body"> <!-- un seul "flow" !!!  -->

            <fo:block font-size="10pt" keep-together="always">
              <fo:block space-after="7pt">Marguerite et Marcel AMBRONIOT</fo:block>
              <fo:block space-after="5pt">Saint André</fo:block>
              <fo:block space-after="37pt">38 256 <fo:inline font-weight="bold">Vianney</fo:inline>
              </fo:block>
            </fo:block>

            <fo:block font-size="10pt" keep-together="always">
              <fo:block space-after="7pt">Monique ALGEBRE</fo:block>
              <fo:block space-after="5pt">4 Allée de la Termite</fo:block>
              <fo:block space-after="37pt">26 537 <fo:inline font-weight="bold">Bourg sous Valence</fo:inline>
              </fo:block>
            </fo:block>

            <fo:block font-size="10pt" keep-together="always">
              <fo:block space-after="7pt">Mireille et Olivier BINÔME</fo:block>
              <fo:block space-after="37pt">26 356 <fo:inline font-weight="bold">St Pierret en Diois</fo:inline>
              </fo:block>
            </fo:block>


            ....... et ainsi de suite .....................................


            <fo:block font-size="10pt" keep-together="always">
              <fo:block space-after="7pt">Bruno et Martine WEYBER</fo:block>
              <fo:block space-after="5pt">Montmartel</fo:block>
              <fo:block space-after="37pt">27 340 <fo:inline font-weight="bold">Soissons</fo:inline>
              </fo:block>
            </fo:block>

          </fo:flow>

        </fo:page-sequence>

      </fo:root>
       

    3. Le fichier pdf produit

      Lorsqu'il est traité par le renderer du logiciel Apache-Fop, il produit un document pdf de 4 pages 21x29,7
      dont voici un aperçu :

      PDF

      On a vu ci-dessus un échantillon d'adresse.

      Voici ce que donne le titre sur la couverture  Titre


  3. Production d'un fichier XML-FO

    1. XSLT

      L'exemple précédent suffit à faire voir qu'un tel fichier doit être produit de manière automatique à partir d'un document plus synthétiqur.
      Le langage XSLT a été conçu précisément dans ce but !

      Le style des transformations vers XSL-FO est le même que celui des transformations vers HTML :
      • une première règle qui met en place le cadre
      • une batterie de règles pour traiter les différentes situations rencontrées dans le document-source.

    2. Mise en œuvre avec Apache-Fop 0.95

      Comme on l'a vu ci-dessus, la mécanique du processeur fop est conçue de manière à intégrer la transformation qui engendre le document XML-FO.
      Supposons que la transformation XML -> FO soit donnée par un fichier nommé trsf.xsl.
      La séquence de code Java présentée ci-dessus pour engendrer du pdf à partir du FO devient :

      String fichOut = le nom du fichier pdf de sortie
      out = new BufferedOutputStream(new FileOutputStream(new File(fichOut)));
      Fop fop = FopFactory.newInstance().newFop(MimeConstants.MIME_PDF, out);
      Result res = new SAXResult(fop.getDefaultHandler());

      String fichIn = le nom du fichier XML-source
      Source src = new StreamSource(new File(fichIn));

      String fichFst = "trsf.xsl";
      Source src_tr = new StreamSource(new File(fichFst));

      Transformer tf = TransformerFactory.newInstance().newTransformer(src_tr);
      transformer.transform(src, res);
      out.close();


      En somme, il s'agit simplement de remplacer un Transformer construit par newTransformer()
      (qui se bornait à convertir le document FO en un SAXResult, en effectuant la transformation identique)
      par un autre construit avec newTransformer(src_tr), qui effectue "au passage" la transformation XML -> FO.
      Dans l'affaire, le document XML-FO disparaît aussitôt créé...

    3. Deuxième exemple

      Variante du précédent, plus simple à certains égards.
      À partir d'un fichier Excel contenant la liste des adhérents à une association, sauvegardé en format CSV,
      on engendre un fichier PDF contenant les adresses des adhérents imprimées en format "étiquette".

      1. XML
        On construit d'abord un fichier XML à partir du CSV (avec DOM),
        voici le début de ce fichier :
        <?xml version="1.0" encoding="UTF-8" standalone="no"?>
        <liste>
           <adherent nom1="AMBRONIOT" nom2="" prenom1="Marguerite" prenom2="Marcel">
              <adresse codePostal="38 256" lieu="Saint André" localite="Vianney"/>
           </adherent>
           <adherent nom1="ALGEBRE" nom2="" prenom1="Monique" prenom2="">
              <adresse codePostal="26 567" lieu="4 Allée de la Termite" localite="Bourg sous Valence"/>
           </adherent>
           <adherent nom1="BINÔME" nom2="" prenom1="Mireille" prenom2="Olivier">
              <adresse codePostal="26 356" lieu="" localite="St Pierret en Diois"/>
           </adherent>

      2. FO
        ce fichier est ensuite transformé en formatting-objects
        sur le modèle ci-dessus par la feuille de style etiquettes.xsl,
        voici le début du fichier :
        <?xml version="1.0" encoding="utf-8"?>
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">

          <fo:layout-master-set>
            <fo:simple-page-master master-name="etiquettes"
              page-height="29.7cm" page-width="21cm"
              margin-top="1cm" margin-bottom="1cm"
              margin-left="1.5cm" margin-right="0.5cm">
              <fo:region-body margin-top="1cm" margin-bottom="0cm" column-count="3"/>
              <fo:region-before extent="3cm"/>
              <fo:region-after extent="1.5cm"/>
            </fo:simple-page-master>
          </fo:layout-master-set>

          <fo:page-sequence master-reference="etiquettes">
            <fo:flow flow-name="xsl-region-body">

              <fo:block font-size="10pt" keep-together="always">
                <fo:block space-after="7pt">Marguerite et Marcel AMBRONIOT</fo:block>
                <fo:block space-after="7pt">Saint André</fo:block>
                <fo:block space-after="67pt">38 256 Vianney</fo:block>
              </fo:block>

              <fo:block font-size="10pt" keep-together="always">
                <fo:block space-after="7pt">Monique ALGEBRE</fo:block>
                <fo:block space-after="7pt">4 Allée de la Termite</fo:block>
                <fo:block space-after="67pt">26 567 Bourg sous Valence</fo:block>
              </fo:block>

              <fo:block font-size="10pt" keep-together="always">
                <fo:block space-after="7pt">Mireille et Olivier BINÔME</fo:block>
                <fo:block space-after="81pt">26 356 St Pierret en Diois</fo:block>
              </fo:block>


      3. et le résultat est directement envoyé au renderer Apache-Fop suivant le procédé détaillé précédemment.

        À titre de comparaison, voici la feuille de style correspondant à notre premier exemple.





  4. Deuxième vague

    1. Séquences à plusieurs types de pages (chapitres, actes, etc)

      1. L'élément page-sequence-master
        est fils direct de layout-master-set
        avec un attribut master-name
        qui joue le même rôle (de master-reference) que son homologue de fo:simple-page-master.
        Il peut y en avoir 0, 1 ou plusieurs, invoqués par les fo:page-sequence via leur attribut master-reference.

        Il décrit la manière dont va se structurer la séquence, en choisissant entre trois possibilités :
        • single-page-master-reference
          qui crée (en principe) une seule page dont le type est donné par son attribut master-reference

        • repeatable-page-master-reference
          qui crée une suite de pages de même type donné par son attribut master-reference

        • repeatable-page-master-alternatives
          qui crée une suite de pages de type choisi via un élément fo:conditional-page-master-reference
          par le couple d'attributs
          1. page-position (ou odd-or-even, ou blank-or-not-blank)
            valeurs possibles :
            • page-position -> first, last, rest, any
            • odd-or-even -> odd, even, any
            • blank-or-not-blank -> blank, not-blank, any
          2. master-reference

      2. Chaque élément page-sequence
        invoque un page-sequence-master qui décrit sa structure.
        Il dispose en outre de différents réglages quant au décompte des pages, voir à l'exemple suivant.

        Pouvons-nous réécrire les page-sequence que nous avons vues précédemment avec des page-sequence-master ?

        • pour le deuxième exemple Adhérents c'est facile :

          <fo:page-sequence-master master-name="les-etiquettes">
              <fo:repeatable-page-master-reference master-reference="etiquettes" />
          </fo:page-sequence-master
          >
          .....
          <fo:page-sequence master-reference="les-etiquettes">
          ... le même contenu que précédemment ...
          </fo:page-sequence>

        • pour le premier ExListe c'est moins clair :
          en effet dans une fo:page-sequence il ya un format et un flow donnant le contenu.
          Or dans ExListe nous avons deux flows différents, il y a donc nécessairement deux fo:page-sequence
          relevant de deux  fo:page-sequence-master différents.

          <fo:page-sequence-master master-name="la-couverture">
              <fo:single-page-master-reference master-reference="
          couverture">
          </fo:page-sequence-master
          >

          <fo:page-sequence-master master-name="les-adhérents">
              <fo:repeatable-page-master-reference master-reference="
          adhérents">
          </fo:page-sequence-master
          >

          .....

          <fo:page-sequence master-reference="
          la-couverture">
          ... le même contenu que précédemment ...
          </fo:page-sequence>

          <fo:page-sequence master-reference="les-adhérents">
          ... le même contenu que précédemment ...
          </fo:page-sequence>

          (Il n'est pas possible de se contenter d'une seule fo:page-sequence relevant d'un fo:page-sequence-master composé
          d'un fo:single-page-master-reference et d'un fo:repeatable-page-master-reference.)


        Contrairement à ce qu'on a vu dans les exemples précédents, les éléments <fo:page-sequence> peuvent être en nombre certes moindre,
        mais aussi indéterminé que les pages elles-mêmes !
        Ils devront donc être produits par XSLT de façon calculée (dynamiquement) et non plus "en dur".

      3. L'exemple des actes dans les pièces de Shakespeare
        On se propose d'imprimer une pièce de Shakespeare donnée en XML sur le site cafeconleche avec les spécifications suivantes :

        • L'impression se fera en recto-verso, on devra donc distinguer les pages paires et impaires.

        • Une page de couverture portera le titre et la liste des personnages.

        • Chaque acte commencera sur une page impaire, les scènes se suivant sans saut de page.

        • Pour faciliter la lecture, les répliques seront imprimées en police Times, les autres indications (titres, didascalies)
          en police Arial (sans-serif), qui est la police par défaut.

        • On demande que le nom du locuteur ne soit jamais séparé de son discours par un saut de page.


        On déduit de ce cahier des charges que trois simple-page-master seront nécessaires :
        1. un pour la page de couverture : <fo:simple-page-master master-name="couverture"...>
        2. un pour les pages impaires : <fo:simple-page-master master-name="impair" ...>
        3. un pour les pages paires : entre ces deux derniers, les marges droites et gauches seront échangées.

        En outre, chaque chapitre sera représenté par une page-sequence dont la structure sera décrite par un page-sequence-master
        qui gèrera l'alternance des pages paires et impaires.

             <fo:page-sequence-master master-name="actes">
                <fo:repeatable-page-master-alternatives>
                    <fo:conditional-page-master-reference
                              odd-or-even="odd" master-reference="impair"/>
                    <fo:conditional-page-master-reference
                              odd-or-even="even"  master-reference="pair"/>
                  </fo:repeatable-page-master-alternatives>
            </fo:page-sequence-master>



        C'est au niveau de la page-sequence que l'on pourra imposer que la première page de chaque acte soit impaire,
        de même qu'on logera le numéro de l'acte dans l'en-tête.
        Plus précisément, le premier acte commencera à la page 1 :

          <fo:page-sequence master-reference="actes" force-page-count="end-on-even" initial-page-number="1">
            <fo:static-content flow-name="xsl-region-before">
              <fo:block text-align="center" font-size="10pt">ACT I</fo:block>
            </fo:static-content>
            <fo:static-content flow-name="xsl-region-after">
              <fo:block text-align="center" font-size="8pt">
                  p. <fo:page-number/></fo:block>
            </fo:static-content>
            <fo:flow flow-name="xsl-region-body">.......



        Les autres seront calés par la seule contrainte  force-page-count="end-on-even" :

          <fo:page-sequence master-reference="actes" force-page-count="end-on-even">
            <fo:static-content flow-name="xsl-region-before">
              <fo:block text-align="center" font-size="10pt">ACT II</fo:block>
            </fo:static-content>
            <fo:static-content flow-name="xsl-region-after">
              <fo:block text-align="center" font-size="8pt">
                  p. <fo:page-number/></fo:block>
            </fo:static-content>
            <fo:flow flow-name="xsl-region-body">.....



        Comme on le voit, il faudra écrire la transformation XSLT de manière à engendrer ce texte FO.
        Cela ne présente pas de difficulté particulière, il suffit d'avoir cette nécessité présente à l'esprit.
        Voici une réalisation possible.

    2. Réglages fins : polices, tailles, espaces...

      Une fois établie l'organisation générale du texte imprimé, reste à mettre au point l'esthétique de la page.
      C'est une affaire qui relève du savoir-faire des typographes et des graphic designers.
      Ce que l'expérience montre rapidement, c'est que cette mise au point passe par le choix d'un grand nombre de paramètres,
      qui se trouvent dispersés dans le texte FO et, par conséquent, dans le code XSLT aussi.
      Voyez l'exemple précédent.

      Il est fortement recommandé de rassembler ces choix en en un tout cohérent, de manière à pouvoir les modifier sans erreur lors des essais.
      L'instrument de choix pour ce faire est de doter le fichier XSLT d'une DTD locale qui va déclarer les différents paramètres en question
      en tant qu'entités. Voici la chose en acte dans la feuille de style etiquettes.xsl.
      <!DOCTYPE stylesheet [
          <!ENTITY taille "10pt">
          <!ENTITY apres1 "81pt">
          <!ENTITY apres2 "67pt">
      ]>