Présentation
Cette macro doit être appelée dans le contenu imbriqué d'une macro hardisFO.foStaticContent. Cette macro permet d'inclure un fragment de document XSL-FO, c'est-à-dire d'autres macros hardisFO.foxxx (ou d'inclure le contenu d'un fichier .ftlx contenant un fragment de document XSL-FO) dans une balise <fo:static-content>, et de mettre à jour automatiquement l'espace physique qui lui est dédié dans le document XSL-FO résultat.
Rappel : dans la définition d'un document XSL-FO, il est possible de déclarer des régions before (en-tête de page), after (pied de page), start (colonne sur le côté "gauche" de la page) et end (colonne sur le côté "droit" de la page).
Pour construire ces régions dans le document, vous devez :
- Déclarer chaque région utilisée dans la balise <fo:simple-page-master> ainsi que sa hauteur et sa largeur grâce à l'attribut "extent". De plus, cette valeur doit se retrouver comme valeur de l'attribut margin-xxx de la balise associée <fo:region-body> (plus précisément la valeur d'extent ne doit pas dépasser la valeur de margin-xxx correspondante),
- Déclarer une balise <fo:static-content> (ainsi que son contenu) et l'associer à la région (via l'attribut "flow-name").
Si le contenu d'une balise <fo:static-content> "dépasse" les dimensions de sa région (c'est-à-dire si la hauteur du contenu est supérieur à la valeur de l'attribut extent), le contenu "déborde" de la balise <fo:static-content> et est visible dans la région body (plus précisément "sous" la région body).
Donc deux difficultés apparaissent dans la création d'un template lorsque le contenu des régions est dynamique :
- Faire en sorte de l'attribut margin-xxx de <fo:region-body> soit "en phase" avec l'attribut "extent" de la balise <fo:region-xxx> (les bonnes pratiques Apache FOP préconisent de déclarer la balise <fo:region-body> avant les autres balises <fo:region-xxx>, donc la macro hardisFo.foRegionBody est exécutée avant les autres macros hardisFo.foRegionxxx),
- Si la balise <fo:static-content> contient du contenu dynamique (donc à hauteur variable), faire en sorte que la hauteur soit "en phase" avec l'attribut "extent" de la balise <fo:region-xxx> associée. Or du fait de la structure d'un document XSL-FO, les macros hardisFo.foRegionxxx sont traitées avant les macros hardisFo.foStaticContent associées.
Cette macro permet de résoudre les problématiques suivantes :
- Rendre du contenu dynamique en le divisant en sections et fixer la visibilité d'une section à l'exécution,
- Fixer dynamiquement les hauteurs des sections,
- Calculer automatiquement la hauteur totale du contenu d'une balise <fo:static-content> et mettre à jour la déclaration des balises <fo :region-xxx> et <fo :region-body> dans le document résultat.
La macro staticSection représente une section et sert de conteneur à un fragment de document XSL-FO. Il est possible d'injecter ce fragment de deux façons :
- Soit en l'insérant comme contenu imbriqué de la macro staticSection,
- Soit en incluant le contenu d'un sous-template .ftlx contenant ce fragment, grâce au paramètre "includeFile". Ce paramètre de type String doit contenir un chemin relatif vers le sous-template. Le chemin est résolu par rapport à l'emplacement du template qui exécute cette macro.
De plus, une macro staticSection peut contenir d'autres macros staticSection sans restriction de profondeur.
On peut résumer ceci par un exemple :
<@hardisFo.foStaticContent flowName="pageHeader"> <#-- On divise dans cet exemple le contenu de <fo:static-content> en deux sections pour definir le header de page --> <@hardisAdv.staticSection> <#-- fragment de document : un block --> <@hardisFo.foBlock>I'm the block of the first section</@hardisFo.foBlock> </@hardisAdv.staticSection> <@hardisAdv.staticSection> <@hardisFo.foBlock>I'm the block of the second section</@hardisFo.foBlock> <@hardisAdv.staticSection includeFile="subtemplate.ftlx"></@hardisAdv.staticSection> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> Et le sous-template subtemplate.ftlx contient le fragment : <@hardisFo.foBlock>I'm the block from the sub template</@hardisFo.foBlock>
Et le sous-template subtemplate.ftlx contient le fragment :
<@hardisFo.foBlock>I'm the block from the sub template</@hardisFo.foBlock>
Visibilité d'une section
Il est possible de conditionner l'affichage d'une macro staticSection c'est-à-dire de permettre d'afficher dynamiquement (à l'exécution du template) son contenu : le paramètre "delete" de type Boolean conditionne l'affichage. La valeur "true" indique que le contenu ne sera pas présent dans le document résultat (et "false" pour une présence du contenu). ↑ Haut de page
Hauteur d'une section
Il est possible de fixer une hauteur à une macro staticSection grâce au paramètre "height". Il doit contenir une valeur de hauteur telle que défini dans la norme XSL 1.1 (cliquez ici pour plus de détails). Lorsque le paramètre "height" est fixé, la macro encapsule son contenu dans une balise <fo :block-container>. Il faut donc dans ce cas s'assurer que la nature du contenu imbriqué peut être insérée dans une balise <fo:block-container> : La spécification XSL 1.1 impose que seules les balises <fo:block>, <fo:block-container>, <fo:table> et <fo:list-block> peuvent être à l'intérieure d'une balise <fo:block-container> (cliquez ici pour plus de détails). Une section non visible (paramètre "delete" à "false") a une hauteur à 0.
Par exemple
<@hardisAdv.staticSection height="2cm"> <@hardisFo.foBlock>Content of section with height set</@hardisFo.foBlock> </@hardisAdv.staticSection>
produit dans le document résultat :
<fo:block-container height="2cm"> <fo:block>Content of section with height set</fo:block> </fo:block-container>
Si le contenu d'une balise <fo:static-content> est entièrement imbriqué dans une ou plusieurs macros staticSection, alors, à l'exécution de celles-ci, il est possible de déduire la hauteur du contenu en additionnant les hauteurs de chaque section. Dans le cas de macros staticSection imbriquées, lorsqu'une macro a une hauteur fixée, on ne prend pas en compte dans le calcul de la hauteur finale les hauteurs des macros imbriquées.
Exemple 1 :
<@hardisFo.foStaticContent> <@hardisAdv.staticSection height="2cm">…</@hardisAdv.staticSection> <@hardisAdv.staticSection height="1cm">…</@hardisAdv.staticSection> <@hardisAdv.staticSection height="4cm" delete=true>…</@hardisAdv.staticSection> </@hardisFo.foStaticContent>
A l'exécution, la hauteur totale est de "2cm + 1cm".
Exemple 2
<@hardisFo.foStaticContent> <@hardisAdv.staticSection> <@hardisAdv.staticSection height="1cm">…</@hardisAdv.staticSection> <@hardisAdv.staticSection height="40mm">…</@hardisAdv.staticSection> </@hardisAdv.staticSection> <@hardisAdv.staticSection height="12pt">…</@hardisAdv.staticSection> <@hardisAdv.staticSection includeFile="subtemplate.ftlx"></@hardisAdv.staticSection> <@hardisAdv.staticSection height="5cm"> <@hardisAdv.staticSection height="10cm">…</@hardisAdv.staticSection> </@hardisAdv.staticSection> </@hardisFo.foStaticContent>
Et le template subtemplate.ftlx contient :
<@hardisAdv.staticSection height="1cm">…</@hardisAdv.staticSection> <@hardisAdv.staticSection height="2.5cm" delete=true>…</@hardisAdv.staticSection>
A l'exécution, la hauteur totale est de "1cm + 40mm + 12pt + 1cm + 5cm". ↑ Haut de page
Mise à jour des régions
Après exécution d'une macro hardisFo.foStaticContent, si celle-ci contient des macros staticSection, la hauteur totale des sections est calculée automatiquement et propagée dans la déclaration de la région associée.
En fonction de la valeur du paramètre flowName (qui doit contenir un nom de région) et de la référence à la séquence de pages maîtresse courante (paramètre masterReference de la macro hardisFo.foPageSequence englobant la macro hardisFo.foStaticContent), il est possible de retrouver la déclaration de la balise <fo:region-xxx> associée ainsi que la balise <fo:region-body> correspondante et d'affecter aux attributs "extent" et "marginxxx" la valeur calculée de la hauteur.
Cette étape ne fonctionne que si les régions concernées n'ont pas de hauteur déjà définie par l'utilisateur : si l'attribut "extent" est présent dans la balise <fo:region-xxx>, sa valeur n'est pas modifiée. De même, l'attribut margin-xxx de la balise <fo:region-body> correspondante n'est pas ajouté.
Par exemple le template :
<?xml version="1.0" encoding="utf-8"?> <@hardisFo.foRoot> <@hardisFo.foLayoutMasterSet> <@hardisFo.foSimplePageMaster masterName="sampleLayout" pageHeight="29.7cm" pageWidth="21cm" marginTop="2.5cm" marginBottom="2.5cm" marginLeft="1cm" marginRight="1cm"> <@hardisFo.foRegionBody/> <@hardisFo.foRegionBefore regionName="headerPage"/> <@hardisFo.foRegionAfter regionName="footerPage"/> </@hardisFo.foSimplePageMaster> </@hardisFo.foLayoutMasterSet> <@hardisFo.foPageSequence masterReference="sampleLayout"> <@hardisFo.foStaticContent flowName="headerPage"> <@hardisAdv.staticSection height="1cm"> <@hardisFo.foBlock>Page header</@hardisFo.foBlock> </@hardisAdv.staticSection> <@hardisAdv.staticSection height="1cm"> <@hardisFo.foBlock>My company</@hardisFo.foBlock> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <@hardisFo.foStaticContent flowName="footerPage"> <@hardisAdv.staticSection height="1cm"> <@hardisFo.foBlock>Page footer</@hardisFo.foBlock> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <@hardisFo.foFlow flowName="xsl-region-body"> <@hardisFo.foBlock>Page body</@hardisFo.foBlock> </@hardisFo.foFlow> </@hardisFo.foPageSequence> </@hardisFo.foRoot>
Produit comme résultat :
<?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="sampleLayout" margin-right="1cm" margin-bottom="2.5cm" page-height="29.7cm" page-width="21cm" margin-top="2.5cm" margin-left="1cm"> <fo:region-body margin-top="1cm+1cm" margin-bottom="1cm" margin-left="0pt" margin-right="0pt"/> <fo:region-before region-name="headerPage" extent="1cm+1cm"/> <fo:region-after region-name="footerPage" extent="1cm"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="sampleLayout"> <fo:static-content flow-name="headerPage"> <fo:block-container height="1cm"> <fo:block>Page header</fo:block> </fo:block-container> <fo:block-container height="1cm"> <fo:block>My company</fo:block> </fo:block-container> </fo:static-content> <fo:static-content flow-name="footerPage"> <fo:block-container height="1cm"> <fo:block>Page footer</fo:block> </fo:block-container> </fo:static-content> <fo:flow flow-name="xsl-region-body"> <fo:block>Page body</fo:block> </fo:flow> </fo:page-sequence> </fo:root>
Détection du type de page maîtresse
Il est intéressant d'utiliser cette fonctionnalité lorsque :
- la mise en page du document XSL-FO s'appuie sur des pages maîtresses conditionnées (cliquez ici pour plus de détails) suivant un critère de position (attribut "pagePosition" valant first, last, rest, any ou only) ou de parité (attribut "oddOrEven" valant even, odd ou any) ou de contenance (attribut "blankOrNotBlank" valant blank, not-blank ou any),
- on veut particulariser, pour une région donnée (par exemple pour l'en-tête de page), le contenu de la première page (ou le contenu des pages paires par exemple).
Une première solution consiste à particulariser le contenu de chaque balise <fo:static-content> correspondant à la région choisie et au type de page maîtresse choisi.
Par exemple
<?xml version="1.0" encoding="utf-8"?> <@hardisFo.foRoot> <@hardisFo.foLayoutMasterSet> <#-- Déclaration des pages maitresse --> <#-- Page maitresse de type position "autre" --> <@hardisFo.foSimplePageMaster masterName="otherPage" pageHeight="29.7cm" pageWidth="21cm" marginTop="1cm" marginBottom="1cm" marginLeft="2.5cm" marginRight="2.5cm"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="otherPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Page maitresse de type position "premiere" --> <@hardisFo.foSimplePageMaster masterName="firstPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="firstPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Page maitresse de type position "derniere" --> <@hardisFo.foSimplePageMaster masterName="lastPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="lastPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Déclaration de la sequence de pages maitresse --> <@hardisFo.foPageSequenceMaster masterName="allPage"> <@hardisFo.foRepeatablePageMasterAlternatives maximumRepeats="no-limit"> <#-- La premiere page du document aura comme page maitresse "firstPage" --> <@hardisFo.foConditionalPageMasterReference masterReference="firstPage" pagePosition="first" /> <#-- La dernière page du document aura comme page maitresse "lastPage" --> <@hardisFo.foConditionalPageMasterReference masterReference="lastPage" pagePosition="last" /> <#-- Les autres pages du document auront comme page maitresse "otherPage" --> <@hardisFo.foConditionalPageMasterReference masterReference="otherPage" pagePosition="rest" /> </@hardisFo.foRepeatablePageMasterAlternatives> </@hardisFo.foPageSequenceMaster> </@hardisFo.foLayoutMasterSet> <@hardisFo.foPageSequence masterReference="allPage"> <#-- Contenu de l'en-tete de page des pages autres --> <@hardisFo.foStaticContent flowName="otherPageHeader"> <@hardisAdv.staticSection> <#-- Particulariser ici le contenu de l'en-tete de page des pages autres --> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Contenu de l'en-tete de page de la première page --> <@hardisFo.foStaticContent flowName="firstHeader"> <@hardisAdv.staticSection> <#-- Particulariser ici le contenu de l'en-tete de page de la premiere page --> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Contenu de l'en-tete de page de la dernière page --> <@hardisFo.foStaticContent flowName="lastHeader"> <@hardisAdv.staticSection> <#-- Particulariser ici le contenu de l'en-tete de page de la derniere page --> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Contenu du corps des pages --> <@hardisFo.foFlow flowName="xsl-region-body">…</@hardisFo.foFlow> </@hardisFo.foPageSequence> </@hardisFo.foRoot>
Si le contenu des trois en-têtes de page (première, dernière et autres) diffère peu, il y aura duplication de fragment de contenu dans les trois macros hardisAdv.staticSection.
Si la duplication de contenu pose problème, une deuxième solution consiste à factoriser le contenu de l'en-tête dans un sous-template et de l'inclure à chaque section via l'attribut "includeFile". De plus, il est possible, dans une macro staticSection, de récupérer dynamiquement le type de page maîtresse liée à la région à l'aide des variables (de type Boolean) suivantes :
Nom de variable |
Description |
hardisAdv.isFirstPage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de position "first" ou "only" ou "any" |
hardisAdv.isLastPage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de position "last" ou "only" ou "any" |
hardisAdv.isMiddlePage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de position "rest" ou "any" |
hardisAdv.isOddPage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de parité "odd" ou "any" |
hardisAdv.isEvenPage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de parité "even" ou "any" |
hardisAdv.isBlankPage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de contenance "blank" ou "any" |
hardisAdv.isNotBlankPage |
"true" si cette macro s'exécute dans une balise <fo:static-content> associée à une page maîtresse ayant un critère de contenance "not-blank" ou "any" |
Par exemple
<?xml version="1.0" encoding="utf-8"?> <@hardisFo.foRoot> <@hardisFo.foLayoutMasterSet> <#-- Déclaration des pages maitresses --> <#-- Page maitresse de type position "autre" --> <@hardisFo.foSimplePageMaster masterName="otherPage" pageHeight="29.7cm" pageWidth="21cm" marginTop="1cm" marginBottom="1cm" marginLeft="2.5cm" marginRight="2.5cm"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="otherPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Page maitresse de type position "premiere" --> <@hardisFo.foSimplePageMaster masterName="firstPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="firstPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Page maitresse de type position "derniere" --> <@hardisFo.foSimplePageMaster masterName="lastPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="lastPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Déclaration de la séquence de pages maitresses --> <@hardisFo.foPageSequenceMaster masterName="allPage"> <@hardisFo.foRepeatablePageMasterAlternatives maximumRepeats="no-limit"> <#-- La premiere page du document aura comme page maitresse "firstPage" --> <@hardisFo.foConditionalPageMasterReference masterReference="firstPage" pagePosition="first" /> <#-- La dernière page du document aura comme page maitresse "lastPage" --> <@hardisFo.foConditionalPageMasterReference masterReference="lastPage" pagePosition="last" /> <#-- Les autres pages du document auront comme page maitresse "otherPage" --> <@hardisFo.foConditionalPageMasterReference masterReference="otherPage" pagePosition="rest" /> </@hardisFo.foRepeatablePageMasterAlternatives> </@hardisFo.foPageSequenceMaster> </@hardisFo.foLayoutMasterSet> <@hardisFo.foPageSequence masterReference="allPage"> <#-- Contenu de l'en-tete de page des pages autres --> <@hardisFo.foStaticContent flowName="otherPageHeader"> <@hardisAdv.staticSection includeFile="headerFirstLastRestPage.ftlx"> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Contenu de l'en-tete de page de la première page --> <@hardisFo.foStaticContent flowName="firstHeader"> <@hardisAdv.staticSection includeFile="headerFirstLastRestPage.ftlx"> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Contenu de l'en-tete de page de la dernière page --> <@hardisFo.foStaticContent flowName="lastHeader"> <@hardisAdv.staticSection includeFile="headerFirstLastRestPage.ftlx"> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Contenu du corps des pages --> <@hardisFo.foFlow flowName="xsl-region-body">…</@hardisFo.foFlow> </@hardisFo.foPageSequence </@hardisFo.foRoot>
Et le template headerFirstLastRestPage.ftlx contient :
<#if hardisAdv.isFirstPage> <#-- Particulariser le contenu pour la première page --> </#if> <#if hardisAdv.isLastPage> <#-- Particulariser le contenu pour la dernière page --> </#if> <#if hardisAdv.isMiddlePage> <#-- Particulariser le contenu pour les autres pages --> </#if>
Directive vide : Non
Paramètres
Boolean |
delete |
Suppression de la section et de son contenu |
Optionnel. La valeur par défaut est "false" |
String |
height |
Hauteur de la section. |
Optionnel. |
String |
includeFile |
Chemin relatif vers un sous-template .ftlx. |
Optionnel |
Par exemple
<#-- Associer la valeur "green" à l'attribut color --> <@hardisFo.foStaticContent flowName="headerPage"> <#-- Creation d'une section conditionnée de hauteur 1cm --> <@hardisAdv.staticSection height="1cm" delete =.data_model.hideCopyright> <@hardisFo.foBlock>© My company 2018</@hardisFo.foBlock> </@hardisAdv.staticSection> <#-- Création d'une section dynamique affichant les auteurs du document avec une ligne d'en-tête et un auteur par ligne --> <#-- Hauteur section=(Nombre d'auteurs + 1) x hauteur ligne --> <#-- Dans la section on fixe la hauteur d'une ligne avec l'attribut lineHeight --> <@hardisAdv.staticSection height =((.data_model.authorsList?size + 1) * 12 + "pt") delete =(.data_model.authorsList?size == 0) lineHeight="12pt"> <@hardisFo.foBlock>Authors of the document:</@hardisFo.foBlock> <#list.data_model.authorsList as aAuthor> <@hardisFo.foBlock>${aAuthor}</@hardisFo.foBlock> </#list> </@hardisAdv.staticSection> </@hardisFo.foStaticContent>