Description
This macro must be called in the nested content of a hardisFO.foStaticContent macro. This macro is used to include an XSL-FO document fragment, i.e. other hardisFO.foxxx macros (or include the content of an .ftlx file containing an XSL-FO document fragment) in a <fo:static-content> tag, and to automatically update the physical space dedicated to it in the XSL-FO result document.
Remember: in an XSL-FO document definition, before (header), after (footer), start (column on the "left" side of the page) and end (column on the "right" side of the page) regions can be declared.
To create these regions in the document, you need to:
- Declare each region used in the <fo:simple-page-master> tag as well as its height and width with the "extent" attribute. This value should be a margin-xxx attribute value of the associated <fo:region-body> tag (more specifically the extent value must not exceed the corresponding margin-xxx value),
- Declare an <fo:static-content> tag (as well as its content) and associate it with the region (via the "flow-name" attribute).
If the content of an <fo:static-content> tag "exceeds" the dimensions of its regions (i.e. if the content is higher than the extent attribute value), the content "overflows" from the <fo:static-content> tag and can be seen in the body region (more specifically "under" the body region).
Two difficulties arise when creating a template when the content of the regions is dynamic:
- Make sure that the margin-xxx attribute of <fo:region-body> is "in line with" the "extent" attribute of the <fo:region-xxx> tag (Apache FOP best practices recommend declaring the <fo:region-body> tag before the other <fo:region-xxx> tags so the hardisFo.foRegionBody macro is executed before the other hardisFo.foRegionxxx macros),
- If the <fo:static-content> tag contains dynamic content (variable height), make sure that the height is "in line with" the "extent" attribute of the associated <fo:region-xxx> tag. Due to the structure of an XSL-FO document, hardisFo.foRegionxxx macros are processed before the associated hardisFo.foStaticContent macros.
This macro is used to solve the following issues:
- Make content dynamic by dividing it into sections and set the visibility of a section when executed,
- Dynamically set section heights,
- Automatically calculate the total height of the content of a <fo:static-content> tag and update the declaration of <fo :region-xxx> and <fo :region-body> tags in the result document.
The staticSection macro represents a section and acts as a container for an XSL-FO document fragment. This fragment can be injected in two ways:
- Either by inserting it as nested content in the staticSection macro,
- Or by including the content of an .ftlx sub-template containing this fragment, using the "includeFile" parameter. This String-type parameter must contain a relative path to the sub-template. The path is resolved in relation to the location of the template which executes this macro.
A staticSection macro may contain other staticSection macros with no depth restriction.
This example provides an illustration:
<@hardisFo.foStaticContent flowName="pageHeader"> <#-- In this example, the <fo:static-content> content is divided into two sections to define the page header --> <@hardisAdv.staticSection> <#-- document fragment: 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> And the subtemplate.ftlx sub-template contains the fragment: <@hardisFo.foBlock>I'm the block from the sub template</@hardisFo.foBlock>
And the subtemplate.ftlx sub-template contains the fragment:
<@hardisFo.foBlock>I'm the block from the sub template</@hardisFo.foBlock>
Section visibility
The display of a staticSection macro can be made conditional, i.e. allow its content to be dynamically displayed (on template execution): the Boolean-type "delete" parameter makes the display conditional. The "true" value indicates that the content will not be present in the result document (and "false" if content will be present). ↑ Top of page
Section height
A height can be set to a staticSection macro using the "height" parameter. It must contain a height value as defined in standard XSL 1.1 (click here for more details). When the "height" parameter is set, the macro encapsulates its content in an <fo :block-container> tag. In this case, you need to make sure that the type of nested content may be inserted in an <fo:block-container> tag: Specification XSL 1.1 states that only <fo:block>, <fo:block-container>, <fo:table> and <fo:list-block> tags can be inside an <fo:block-container> tag (click here for more details). An invisible section ("delete" parameter is "false") has a height of 0.
For example
<@hardisAdv.staticSection height="2cm"> <@hardisFo.foBlock>Content of section with height set</@hardisFo.foBlock> </@hardisAdv.staticSection>
produces in the result document:
<fo:block-container height="2cm"> <fo:block>Content of section with height set</fo:block> </fo:block-container>
If the content of an <fo:static-content> tag is fully nested in one or more staticSection macros, when they are executed, the height of the content can be deduced by adding the heights of each section. In the case of nested staticSection macros, when a macro has a set height, the heights of nested macros are not taken into account in the final height calculation.
Example 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>
On execution, the total height is "2cm + 1cm".
Example 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>
And the subtemplate.ftlx template contains:
<@hardisAdv.staticSection height="1cm">...</@hardisAdv.staticSection> <@hardisAdv.staticSection height="2.5cm" delete=true>...</@hardisAdv.staticSection>
On execution, the total height is "1cm + 40mm + 12pt + 1cm + 5cm". ↑ Top of page
Region updating
After executing a hardisFo.foStaticContent macro, if it contains staticSection macros, the total height of the sections is calculated automatically and propagated in the associated region declaration.
According to the value of the flowName parameter (which must contain a region name) and the reference to the current page master sequence (masterReference parameter of the hardisFo.foPageSequence macro encompassing the hardisFo.foStaticContent macro), it is possible to find the associated <fo:region-xxx> tag declaration as well as the corresponding <fo:region-body> tag and assign the calculated height value to "extent" and "marginxxx" attributes.
This step only works if the regions concerned do not have a height already defined by the user: if the "extent" attribute is present in the <fo:region-xxx> tag, its value is not changed. Likewise, the margin-xxx attribute of the corresponding <fo:region-body> tag is not added.
For example, the 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>
Produces as a result:
<?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>
Page master type detection
It is useful to use this feature when:
- The XSL-FO document layout uses conditional page masters (click here for more details) according to a position criterion ("pagePosition" attribute is first, last, rest, any or only), parity criterion ("oddOrEven" attribute is even, odd or any) or content criterion ("blankOrNotBlank" attribute is blank, not-blank or any),
- We want to specify the content of the first page (or content of even pages for example) for a given region (e.g. for the page header).
An initial solution involves specifying the content of each <fo:static-content> tag corresponding to the chosen region and chosen page master type.
For example
<?xml version="1.0" encoding="utf-8"?> <@hardisFo.foRoot> <@hardisFo.foLayoutMasterSet> <#-- Page master declaration --> <#-- "other" position-type page master --> <@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> <#-- "first" position type page master --> <@hardisFo.foSimplePageMaster masterName="firstPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="firstPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- "last" position type page master --> <@hardisFo.foSimplePageMaster masterName="lastPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="lastPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Page master sequence declaration --> <@hardisFo.foPageSequenceMaster masterName="allPage"> <@hardisFo.foRepeatablePageMasterAlternatives maximumRepeats="no-limit"> <#-- The first page of the document will have "firstPage" as a page master --> <@hardisFo.foConditionalPageMasterReference masterReference="firstPage" pagePosition="first" /> <#-- The last page of the docuement will have "lastPage" as a page master --> <@hardisFo.foConditionalPageMasterReference masterReference="lastPage" pagePosition="last" /> <#-- The other pages of the document will have "otherPage" as a page master --> <@hardisFo.foConditionalPageMasterReference masterReference="otherPage" pagePosition="rest" /> </@hardisFo.foRepeatablePageMasterAlternatives> </@hardisFo.foPageSequenceMaster> </@hardisFo.foLayoutMasterSet> <@hardisFo.foPageSequence masterReference="allPage"> <#-- Content of the page header of other pages --> <@hardisFo.foStaticContent flowName="otherPageHeader"> <@hardisAdv.staticSection> <#-- Specify the content of the page header of other pages here --> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Content of the page header of the first page --> <@hardisFo.foStaticContent flowName="firstHeader"> <@hardisAdv.staticSection> <#-- Specify the content of the page header of the first page here --> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Content of the page header of the last page --> <@hardisFo.foStaticContent flowName="lastHeader"> <@hardisAdv.staticSection> <#-- Specify the content of the page header of the last page here --> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Page body content --> <@hardisFo.foFlow flowName="xsl-region-body">...</@hardisFo.foFlow> </@hardisFo.foPageSequence> </@hardisFo.foRoot>
If the content of three page headers (first, last and others) does not differ significantly, there will be content fragment duplication in the three hardisAdv.staticSection macros.
If content duplication causes a problem, a second solution involves factorizing the header content in a sub-template and including it in each section via the "includeFile" attribute. In a staticSection macro, the page master type linked to the region can be retrieved using the following (Boolean-type) variables:
Variable name |
Description |
hardisAdv.isFirstPage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with a "first" or "only" or "any" position criterion. |
hardisAdv.isLastPage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with a "last" or "only" or "any" position criterion. |
hardisAdv.isMiddlePage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with a "rest" or "any" position criterion. |
hardisAdv.isOddPage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with an "odd" or "any" parity criterion. |
hardisAdv.isEvenPage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with an "even" or "any" parity criterion. |
hardisAdv.isBlankPage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with a "blank" or "any" content criterion. |
hardisAdv.isNotBlankPage |
"true" if this macro is executed in an <fo:static-content> tag associated with a page master with a "not-blank" or "any" content criterion. |
For example
<?xml version="1.0" encoding="utf-8"?> <@hardisFo.foRoot> <@hardisFo.foLayoutMasterSet> <#-- Page master declaration --> <#-- "other" position-type page master --> <@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> <#-- "first" position type page master --> <@hardisFo.foSimplePageMaster masterName="firstPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="firstPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- "last" position type page master --> <@hardisFo.foSimplePageMaster masterName="lastPage"> <@hardisFo.foRegionBody /> <@hardisFo.foRegionBefore regionName="lastPageHeader" /> </@hardisFo.foSimplePageMaster> <#-- Page master sequence declaration --> <@hardisFo.foPageSequenceMaster masterName="allPage"> <@hardisFo.foRepeatablePageMasterAlternatives maximumRepeats="no-limit"> <#-- The first page of the document will have "firstPage" as a page master --> <@hardisFo.foConditionalPageMasterReference masterReference="firstPage" pagePosition="first" /> <#-- The last page of the document will have "lastPage" as a page master --> <@hardisFo.foConditionalPageMasterReference masterReference="lastPage" pagePosition="last" /> <#-- The other pages of the document will have "otherPage" as a page master --> <@hardisFo.foConditionalPageMasterReference masterReference="otherPage" pagePosition="rest" /> </@hardisFo.foRepeatablePageMasterAlternatives> </@hardisFo.foPageSequenceMaster> </@hardisFo.foLayoutMasterSet> <@hardisFo.foPageSequence masterReference="allPage"> <#-- Content of the page header of other pages --> <@hardisFo.foStaticContent flowName="otherPageHeader"> <@hardisAdv.staticSection includeFile="headerFirstLastRestPage.ftlx"> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Content of the page header of the first page --> <@hardisFo.foStaticContent flowName="firstHeader"> <@hardisAdv.staticSection includeFile="headerFirstLastRestPage.ftlx"> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Content of the page header of the last page --> <@hardisFo.foStaticContent flowName="lastHeader"> <@hardisAdv.staticSection includeFile="headerFirstLastRestPage.ftlx"> </@hardisAdv.staticSection> </@hardisFo.foStaticContent> <#-- Page body content --> <@hardisFo.foFlow flowName="xsl-region-body">...</@hardisFo.foFlow> </@hardisFo.foPageSequence </@hardisFo.foRoot>
And the headerFirstLastRestPage.ftlx template contains:
<#if hardisAdv.isFirstPage> <#-- Specify the content for the first page --> </#if> <#if hardisAdv.isLastPage> <#-- Specfify the content for the last page --> </#if> <#if hardisAdv.isMiddlePage> <#-- Specify the content for the other pages --> </#if>
Empty directive: No
Parameters
Boolean |
delete |
Deletion of section and its content |
Optional. The default value is "false" |
String |
height |
Section height. |
Optional. |
String |
includeFile |
Relative path to an .ftlx sub-template. |
Optional |
For example
?<#-- Associate the "green" value to the color attribute --> <@hardisFo.foStaticContent flowName="headerPage"> <#-- Create a 1cm high conditional section --> <@hardisAdv.staticSection height="1cm" delete =.data_model.hideCopyright> <@hardisFo.foBlock>? My company 2018</@hardisFo.foBlock> </@hardisAdv.staticSection> <#-- Create a dynamic section displaying the document authors with a header line and one author per line --> <#-- Section height=(Number of authors + 1) x line height --> <#-- In the section, the height of a line is set with the lineHeight attribute --> <@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>