Using node-set with Antenna House XSL-FO XSLT - xslt

I would like to use node-set() in Antenna House so I can access preceding-siblings in a sorted list. (I'm trying to follow this example: [using preceding-sibling with with xsl:sort)
I'm not sure what the syntax is for declaring the namespace to access node-set(). AH is not giving any errors, but my call to node-set() fails. I've tried:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" extension-element-prefixes="msxsl" version="1.0">
Here is the XML:
<illustratedPartsCatalog>
<figure id="fig1">...</figure>
<catalogSeqNumber assyCode="00" figureNumber="01" indenture="0" item="000" itemVariant="A" subSubSystemCode="0" subSystemCode="0" systemCode="00">
<itemSeqNumber itemSeqNumberValue="000">
<quantityPerNextHigherAssy>XX</quantityPerNextHigherAssy>
<partRef manufacturerCodeValue="00000" partNumberValue="11111-111">
</partRef>
<partSegment>
<itemIdentData>
<descrForPart>VALVE ASSEMBLY</descrForPart></itemIdentData>
</partSegment><applicabilitySegment><usableOnCodeAssy>X</usableOnCodeAssy>
</applicabilitySegment></itemSeqNumber></catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<figure id="fig2">...</figure>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
<catalogSeqNumber>...</catalogSeqNumber>
</illustratedPartsCatalog>
XSLT:
<xsl:variable name="sortedCSN">
<xsl:for-each select="illustratedPartsCatalog/catalogSeqNumber">
<xsl:sort select="concat(itemSeqNumber/partRef/#partNumberValue, #figureNumber,#item)"/>
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
<xsl:template name="SortParts2" >
<xsl:for-each select="msxsl:node-set($sortedCSN)/catalogSeqNumber">
<xsl:sort select="concat(itemSeqNumber/partRef/#partNumberValue, #figureNumber,#item)"/>
<xsl:call-template name="catalogSeqNumber-NI">
<xsl:with-param name="figNo" select="concat(#figureNumber,#figureNumberVariant)"/>
<xsl:with-param name="prfigNo" select="concat(preceding-sibling::catalogSeqNumber/#figureNumber,preceding-sibling::catalogSeqNumber/#figureNumberVariant)" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="catalogSeqNumber-NI">
<xsl:param name="figNo"/>
<xsl:param name="prfigNo" />
<fo:table-row keep-together.within-page="always" wrap-option="wrap">
<fo:table-cell xsl:use-attribute-sets="table.cell.padding" text-transform="uppercase" wrap-option="wrap">
<fo:block wrap-option="wrap">
<xsl:value-of select=" ./itemSeqNumber/partRef/#partNumberValue"/>
</fo:block>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="table.cell.padding" text-align="start">
<xsl:choose>
<xsl:when test="$figNo">
<fo:block>
<xsl:text> </xsl:text><xsl:value-of select="$figNo"/><xsl:text> </xsl:text> <xsl:value-of select="$prfigNo"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block />
</xsl:otherwise>
</xsl:choose>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="table.cell.padding" text-align="start">
<fo:block>
<xsl:if test="./itemSeqNumber/partLocationSegment/notIllustrated">
<xsl:text>-</xsl:text>
</xsl:if>
<xsl:choose>
<xsl:when test="#item">
<xsl:variable name="itemNo">
<xsl:call-template name="suppressZero" >
<xsl:with-param name="pText" select="#item"/>
</xsl:call-template>
</xsl:variable>
<xsl:text> </xsl:text>
<xsl:value-of select="concat($itemNo,#itemVariant)"/>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</fo:block>
</fo:table-cell>
<fo:table-cell xsl:use-attribute-sets="table.cell.padding">
<fo:block>
<xsl:value-of select="./itemSeqNumber/quantityPerNextHigherAssy"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>

I think the default for Antenna House on Windows is to use MSXML so the attempt with xmlns:msxsl="urn:schemas-microsoft-com:xslt" is fine as far as using the node-set extension function.
I think you simply need to change the XSLT to
<xsl:variable name="sortedCSN">
<xsl:for-each select="illustratedPartsCatalog/catalogSeqNumber">
<xsl:sort select="concat(itemSeqNumber/partRef/#partNumberValue, #figureNumber,#item)"/>
<xsl:copy-of select="." />
</xsl:for-each>
</xsl:variable>
<xsl:template name="SortParts2" >
<xsl:for-each select="msxsl:node-set($sortedCSN)/catalogSeqNumber">
<xsl:sort select="concat(itemSeqNumber/partRef/#partNumberValue, #figureNumber,#item)"/>
<xsl:call-template name="catalogSeqNumber-NI">
<xsl:with-param name="figNo" select="concat(#figureNumber,#figureNumberVariant)"/>
<xsl:with-param name="prfigNo" select="concat(preceding-sibling::catalogSeqNumber/#figureNumber,preceding-sibling::catalogSeqNumber/#figureNumberVariant)" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
to make sense with the input you have shown (as far as you have shown it, can't judge all those xsl:sort attempts without seeing the data to be sorted).
On the other hand, if you get errors about the stylesheet not being correct XSLT or XML, you would better show us a minimal but complete stylesheet allowing us to reproduce the problem.

Related

Need to dynamically change the simple-page-master's master-name

I have an xml document as shown below. Each repeating doc is a page in PDF file
<AFPXMLFile>
<docs>
<regList>
<region>1</region>
<secList>
<col>1</col>
<lines></lines>
</secList>
</regList>
<regList>
<region>2</region>
<secList>
<col>2</col>
<lines>
<line>IBM BELGIUM xxx</line>
<line>xxxxxx</line>
<line>xxxx</line>
</lines>
</secList>
</regList>
<regList></regList>
<regList></regList>
</docs>
<docs></docs>
</AFPXMLFile>
My XSL is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"
exclude-result-prefixes="fo">
<xsl:template match="AFPXMLFile">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master page-width="21cm" page-height="29.7cm" margin-top="1.27cm" margin-bottom="1.27cm" margin-left="1.75cm" master-name="A4">
<fo:region-body margin-top="1mm" margin-bottom="1mm"/>
<fo:region-before extent="0mm"/>
<fo:region-after extent="0mm"/>
<fo:region-start writing-mode="tb-rl" precedence="true" extent="10mm" />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="A4" font-family="sans-serif">
<xsl:for-each select="docs">
<xsl:for-each select="./regList">
<xsl:choose>
<xsl:when test="region = 1">
<fo:static-content flow-name="xsl-region-before">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="purple">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = 2">
<fo:static-content flow-name="xsl-region-start">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="4pt" padding-before="4pt" text-align="left" color="green">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = 3">
<fo:static-content flow-name="xsl-region-body">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="blue">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = 4">
<fo:flow flow-name="xsl-region-after">
<xsl:for-each select="./secList">
<xsl:for-each select="./lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="orange">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:flow>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
When I run the transformation I get the following error:
org.apache.fop.fo.ValidationException: For "fo:page-sequence", "fo:static-content" must be declared before "fo:flow"! (No context info available)
This I suspect because it is repeating the static content region for each page. So I believe I need to change the simple-page-master:master-name for every page
I need help with that.
According to the XSL-FO recommendation, the contents of 'fo:page-sequence' is:
(title?,folio-prefix?,folio-suffix?,static-content*,flow+)
which means that all the fo:static-content elements must be before the fo:flow elements.
Your stylesheet dynamically creates either fo:static-content (if region is 1, 2 or 3) or fo:flow (if region is 4). From the reduced input file in your question it is not possible to see whether the regList elements are alway correctly sorted, so that they produce a valid output when sequentially processed.
Moreover, as each docs element represents a different document with different headers/footers, you have to create distinct fo:page-sequences instead of a single one (otherwise you get too many static contents and flows for a single page sequence):
<xsl:for-each select="docs">
<fo:page-sequence master-reference="A4" font-family="sans-serif">
<xsl:for-each select="./regList">
....
</xsl:for-each>
</fo:page-sequence>
</xsl:for-each>
Moreover, there is a strange thing in the stylesheet: you map an fo:static-content to the xsl-region-body region, and the fo:flow to the xsl-region-after region, which is quite unusual. If the "real" content for the body region is the one with region = 3, you must process region 1, 2 and 4 first, and then region 3:
<xsl:for-each select="docs">
<fo:page-sequence master-reference="A4" font-family="sans-serif">
<!-- create static contents -->
<xsl:apply-templates select="./regList[region != '3']"/>
<!-- create flow -->
<xsl:apply-templates select="./regList[region = '3']"/>
</fo:page-sequence>
</xsl:for-each>
with an additional template to match reglist:
<xsl:template match="regList">
<xsl:choose>
<xsl:when test="region = '1'">
<fo:static-content flow-name="xsl-region-before">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="purple">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = '2'">
<fo:static-content flow-name="xsl-region-start">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="4pt" padding-before="4pt" text-align="left" color="green">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
<xsl:when test="region = '3'">
<fo:flow flow-name="xsl-region-body">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="blue">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:flow>
</xsl:when>
<xsl:when test="region = '4'">
<fo:static-content flow-name="xsl-region-after">
<xsl:for-each select="./secList">
<xsl:for-each select="./lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="orange">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:when>
</xsl:choose>
</xsl:template>
or several, smaller templates:
<xsl:template match="reglist[region = '1']">
<fo:static-content flow-name="xsl-region-before">
<xsl:for-each select="./secList/lines">
<xsl:for-each select="node()">
<fo:block font-size="8pt" color="purple">
<xsl:value-of select="."/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</fo:static-content>
</xsl:template>
<xsl:template match="reglist[region = '2']">
...
</xsl:template>
...

Table of Contents XSL-FO XSLT 1.0

XML:
<levelledPara><title>Tools List and Tool Illustrations</title>
<levelledPara><title>General</title>
<levelledPara><para>The special tools, fixtures, and equipment needed.</para></levelledPara></levelledPara>
XSLT:
<xsl:template match="levelledPara" name="levelledPara" mode="tocdm">
<xsl:if test="*[self::title] and not(parent::*[self::levelledPara])">
<xsl:variable name="id">
<xsl:call-template name="para.id"/>
</xsl:variable>
<fo:table-row>
<fo:table-cell xsl:use-attribute-sets="table.cell.padding1" number-columns-spanned="2">
<fo:block text-transform="capitalize" text-align-last="justify" text-indent="21mm">
<xsl:number count="levelledPara" from="content" level="multiple" format="1.1.1.1.1"/>
<xsl:text>   </xsl:text>
<xsl:value-of select="title" /><fo:leader leader-pattern="dots"/><fo:basic-link internal-destination="{$id}"><fo:page-number-citation ref-id="{$id}"/></fo:basic-link>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
</xsl:template>
<xsl:template match="levelledPara">
<fo:list-block
provisional-distance-between-starts="21mm"
provisional-label-separation="4pt">
<fo:list-item space-after="8pt" space-before="13pt" start-indent="0pt">
<xsl:variable name="id">
<xsl:if test="*[self::title] and not(parent::*[self::levelledPara])">
<xsl:call-template name="para.id"/>
</xsl:if>
</xsl:variable>
<fo:list-item-label
end-indent="label-end()"
text-align="start">
<fo:block font-weight="bold" id="{$id}">
<xsl:if test="not(./table)">
<xsl:number count="levelledPara" from="content" level="multiple" format="1.1.1.1.1"/>
</xsl:if>
</fo:block>
</fo:list-item-label>
<fo:list-item-body
start-indent="body-start()">
<xsl:apply-templates/>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</xsl:template>
<xsl:template name="para.id">
<xsl:param name="object" select="."/>
<xsl:apply-templates select="ancestor::dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode"/>
<xsl:choose>
<xsl:when test="$object/#id">
<xsl:value-of select="$object/#id"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(count(ancestor::node()),'00000000',count(preceding::node()))"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The first titled levelledPara should be included in the Table of Contents. In my sample markup none have IDs. The page number wasn't resolving because I forgot to assign an id to the fo:block for levelledPara.
You've shown the XSLT for the table of contents. The ID in the TOC should match an ID in the main text of your document.
So the template match="levelledPara" should contain a block that sets the ID:
<xsl:variable name="lpcode"><xsl:value-of select="$dmcode" /><xsl:value-of select="generate-id(.)" /></xsl:variable>
<fo:block id="{$lpcode}">

XSL:FO Different list-item-label per level

I'm using Apache FOP to generate PDFs from my web application, where users can edit richtext using CKEditor.
My problem is that users sometimes use different levels of indentation in (un-)ordered lists, e.g.:
List item level 1
List item level 2
The CKEditor shows different bulletins per level (or indentation), but the generated PDFs do not, because my template looks like this:
<!-- Lists -->
<xsl:template match="ul|ol" mode="content">
<xsl:apply-templates select="li" mode="content">
<xsl:with-param name="ordered">
<xsl:value-of select="name()='ol'"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="li" mode="content">
<xsl:param name="ordered"/>
<xsl:variable name="label">
<xsl:choose>
<xsl:when test="$ordered='true'">
<xsl:value-of select="position()"/>
.
</xsl:when>
<xsl:otherwise>
•
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:list-block padding-bottom="0pt">
<fo:list-item>
<fo:list-item-label>
<fo:block>
<xsl:value-of select="$label"/>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates mode="content"/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</xsl:template>
So how can I set the label variable depending on which level of indentation I'm at?
Something like:
1st level: &#8226
2nd level: &#9702
3rd level: &#8269
Thanks in advance, ~Fabi
EDIT: So the solution suggested by #fafl looks like this:
<!-- Lists -->
<xsl:template match="ul|ol" mode="content">
<xsl:param name="depth" select="0"/>
<xsl:apply-templates select="li" mode="content">
<xsl:with-param name="ordered">
<xsl:value-of select="name()='ol'"/>
</xsl:with-param>
<xsl:with-param name="depth">
<xsl:value-of select="$depth + 1"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="li" mode="content">
<xsl:param name="depth" select="0"/>
<xsl:param name="ordered"/>
<xsl:variable name="label">
<xsl:choose>
<xsl:when test="$ordered='true'">
<xsl:value-of select="position()"/>
.
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$depth = 1">
• <!--filled circle-->
</xsl:when>
<xsl:when test="$depth = 2">
◦ <!--not-filled circle-->
</xsl:when>
<xsl:otherwise>
■ <!--black square -->
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:list-block padding-bottom="0pt">
<fo:list-item>
<fo:list-item-label>
<fo:block>
<xsl:value-of select="$label"/>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates mode="content">
<xsl:with-param name="depth">
<xsl:value-of select="$depth"/>
</xsl:with-param>
</xsl:apply-templates>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</xsl:template>
Try adding a parameter "depth" to both templates with a default value of 1. On each recursive call of "ul|ol" increase it by 1. Then you can query it inside both templates.

<fo:basic-link> not creating clickable link XSL-FO XSLT 1.0

Given this XML:
<figure id="fig-0011">
<title>Removal of Shipping Kit(s)</title>
<graphic id="fig-0011-gra-0001" infoEntityIdent="66503-00129-A-001-01"></graphic>
</figure>
and
<proceduralStep><para>Remove screw (see <internalRef internalRefId="fig-0011" internalRefTargetType="irtt01"></internalRef>)
<proceduralStep><para>Remove two screws (10) (see <internalRef internalRefId="fig-0011-gra-0001" internalRefTargetType="irtt09"></internalRef>),
I'm trying to create links to the figures and graphics. The link to the figure is working correctly, but the link to the graphic is not:
<fo:basic-link>
<xsl:attribute name="internal-destination"><xsl:apply-templates select="//dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode"/><xsl:value-of select="#internalRefId"/></xsl:attribute>
<xsl:variable name="targetElement" select="local-name(key('id',#internalRefId))"/>
<xsl:for-each select="key('id',(#internalRefId))">
<xsl:choose>
<xsl:when test="$targetElement='graphic'">
<xsl:text>Fig </xsl:text>
<xsl:number count="figure" from="content" level="any"/>
</xsl:when>
<xsl:when test="$targetElement='figure'">
<xsl:text>Fig </xsl:text>
<xsl:number count="figure" from="content" level="any"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
Key:
<xsl:key name="id" match="*" use="#id"/>
Figure:
<xsl:template match="figure">
<xsl:apply-templates select="graphic"/>
</xsl:template>
<xsl:template match="figure/graphic" priority="10">
<fo:block text-align="center" start-indent="0pt" color="black" keep-with-next="always">
<xsl:call-template name="do-graphic">
<xsl:with-param name="include-graphic" select="true()"/>
</xsl:call-template>
</fo:block>
<xsl:if test="position()=last()">
<xsl:apply-templates select="../legend"/>
</xsl:if>
<xsl:if test="#infoEntityIdent">
<fo:block text-align="right" keep-with-previous="always">
<xsl:value-of select="#infoEntityIdent"/>
</fo:block>
</xsl:if>
<fo:block font-weight="normal" font-style="italic" text-align="center" space-before="12pt" space-after="8pt" start-indent="0pt" keep-with-previous="always">
<xsl:if test="../#id">
<xsl:attribute name="id"><xsl:apply-templates select="//dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode"/><xsl:value-of select="../#id"/></xsl:attribute>
</xsl:if>
<xsl:text>Fig </xsl:text>
<xsl:number count="figure" level="any" format="1" from="content"/>
<xsl:apply-templates select="../title"/>
<xsl:variable name="numSheets" select="count(../graphic)"/>
<xsl:choose>
<xsl:when test="$numSheets>1">
<xsl:text> (Sheet </xsl:text>
<xsl:number count="graphic" level="any" format="1" from="figure"/>
<xsl:text> of </xsl:text>
<xsl:value-of select="$numSheets"/>
<xsl:text>)</xsl:text>
</xsl:when>
</xsl:choose>
</fo:block>
Graphic:
<xsl:template match="graphic">
<fo:inline-container>
<fo:block space-before="12pt" space-before.conditionality="retain">
<xsl:call-template name="do-graphic"/>
</fo:block>
</fo:inline-container>
</xsl:template>
<xsl:template name="do-graphic">
<xsl:param name="include-graphic" select="true()"/>
<xsl:variable name="content-width">
<xsl:choose>
<xsl:when test="#reproductionWidth != ''">
<xsl:value-of select="#reproductionWidth"/>
</xsl:when>
<!-- coding for reproduction scale based on clarifications in Issue 4.1 -->
<xsl:when test="#reproductionScale != ''">
<xsl:value-of select="#reproductionScale"/>
<xsl:text>%</xsl:text>
</xsl:when>
<xsl:otherwise>95%</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="content-height">
<xsl:choose>
<xsl:when test="#reproductionHeight != ''">
<xsl:value-of select="#reproductionHeight"/>
</xsl:when>
<xsl:when test="#reproductionScale != ''">
<xsl:value-of select="#reproductionScale"/>
<xsl:text>%</xsl:text>
</xsl:when>
<xsl:otherwise>95%</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$include-graphic">
<fo:external-graphic src="{unparsed-entity-uri(#infoEntityIdent)}" scaling="uniform" content-height="{$content-height}" content-width="{$content-width}"/>
</xsl:when>
<xsl:otherwise>
<fo:inline-container display-align="center" text-align="center" block-progression-dimension="{$content-height}" inline-progression-dimension="{$content-width}" background-color="silver" color="red">
<fo:block>Graphic Not Included</fo:block>
</fo:inline-container>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This returns:
Remove two screws (10) (see Fig 2)
which is the correct figure number, but the 2 is not a clickable link to the graphic.
The dmcode template builds a string. If I remove that from the XSLT then the figure number no longer links to the figure.
I am using XSLT 1.0. I appreciate any help, thank you.
The second internalRef refers to a graphic. It appears that your XSLT is not copying the graphic/#id into the FO in the result tree. With AH Formatter and a stylesheet that doesn't copy graphic/#id, I get no link and the error message:
Unresolved internal-destination: "fig-0011-gra-0001".
It appears that your FO formatter also isn't making the link when there's no matching target ID.
I can't help you much more without seeing your xsl:key declaration(s), the templates for figure and graphic, and knowing whether graphic can appear outside of figure.
Reedited to use figure/#id and graphic/#id:
<xsl:key name="id" match="*" use="#id" />
<xsl:variable name="prefix">
<xsl:apply-templates select="//dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode" />
</xsl:variable>
<xsl:template match="internalRef">
<xsl:for-each select="key('id', #internalRefId)">
<fo:basic-link internal-destination="{$prefix}{#id}">
<xsl:text>Fig </xsl:text>
<xsl:number count="figure" from="content" level="any" />
</fo:basic-link>
</xsl:for-each>
</xsl:template>
<xsl:template match="figure">
<fo:wrapper id="{#id}">
<xsl:apply-templates select="graphic" />
</fo:wrapper>
</xsl:template>
<xsl:template match="figure/graphic" priority="10">
<fo:block text-align="center" start-indent="0pt" color="black" keep-with-next="always">
<xsl:if test="#id">
<xsl:attribute name="id">
<xsl:value-of select="$prefix" />
<xsl:value-of select="#id" />
</xsl:attribute>
</xsl:if>
<xsl:call-template name="do-graphic">
<xsl:with-param name="include-graphic" select="true()" />
</xsl:call-template>
</fo:block>
<xsl:if test="position() = last()">
<xsl:apply-templates select="../legend" />
</xsl:if>
<xsl:if test="#infoEntityIdent">
<fo:block text-align="right" keep-with-previous="always">
<xsl:value-of select="#infoEntityIdent" />
</fo:block>
</xsl:if>
<fo:block font-weight="normal" font-style="italic" text-align="center" space-before="12pt" space-after="8pt" start-indent="0pt" keep-with-previous="always">
<xsl:text>Fig </xsl:text>
<xsl:number count="figure" level="any" format="1" from="content" />
<xsl:apply-templates select="../title" />
<xsl:variable name="numSheets" select="count(../graphic)" />
<xsl:choose>
<xsl:when test="$numSheets > 1">
<xsl:text> (Sheet </xsl:text>
<xsl:number count="graphic" level="any" format="1" from="figure" />
<xsl:text> of </xsl:text>
<xsl:value-of select="$numSheets" />
<xsl:text>)</xsl:text>
</xsl:when>
</xsl:choose>
</fo:block>
</xsl:template>
I also moved the graphic's ID to the first fo:block in the figure/graphic template. Having it on the last fo:block would have meant scrolling up to see the graphic every time that you followed a link to a graphic.
It was suggested to add <xsl:attribute name="id"> to the <fo:external-graphic> in the do-graphic template and that seems to solve the problem:
<xsl:template name="do-graphic">
<xsl:param name="include-graphic" select="true()"/>
<xsl:variable name="content-width">
<xsl:choose>
<xsl:when test="#reproductionWidth != ''">
<xsl:value-of select="#reproductionWidth"/>
<xsl:call-template name="checkForUom">
<xsl:with-param name="measure" select="#reproductionWidth"/>
</xsl:call-template>
</xsl:when>
<!-- coding for reproduction scale based on clarifications in Issue 4.1 -->
<xsl:when test="#reproductionScale != ''">
<xsl:value-of select="#reproductionScale"/>
<xsl:text>%</xsl:text>
</xsl:when>
<xsl:otherwise>95%</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="content-height">
<xsl:choose>
<xsl:when test="#reproductionHeight != ''">
<xsl:value-of select="#reproductionHeight"/>
<xsl:call-template name="checkForUom">
<xsl:with-param name="measure" select="#reproductionWidth"/>
</xsl:call-template>
</xsl:when>
<!-- coding for reproduction scale based on clarifications in Issue 4.1 -->
<xsl:when test="#reproductionScale != ''">
<xsl:value-of select="#reproductionScale"/>
<xsl:text>%</xsl:text>
</xsl:when>
<xsl:otherwise>95%</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$include-graphic">
<fo:external-graphic src="{unparsed-entity-uri(#infoEntityIdent)}" scaling="uniform" content-height="{$content-height}" content-width="{$content-width}">
<xsl:attribute name="id"><xsl:apply-templates select="//dmodule/identAndStatusSection/dmAddress/dmIdent/dmCode"/><xsl:value-of select="#id"/></xsl:attribute>
</fo:external-graphic>
</xsl:when>
<xsl:otherwise>
<fo:inline-container display-align="center" text-align="center" block-progression-dimension="{$content-height}" inline-progression-dimension="{$content-width}" background-color="silver" color="red">
<fo:block>Graphic Not Included</fo:block>
</fo:inline-container>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

How do I insert spacing/padding in XSL-FO generated PDF for fields without any value?

I have the following XML:
<sample>
<value1>This is one</value1>
<value2>Number two</value2>
<value4>Last value</value4>
</sample>
Using Apache FOP/XSL-FO I would like a PDF looking similar to this:
Value 1: This is one Value 2: Number two
Value 3: Value 4: Last value
Notice the spacing/padding between "Value 3:" and "Value 4:".
The following transformation gives my the result I want. But it seems overly complicated (and might not perform very well for a real-life PDF with many values).
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="sample">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="page-layout">
<fo:region-body margin="10mm" region-name="body"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="page-layout">
<fo:flow flow-name="body">
<fo:block>
<xsl:variable name="pad">
<xsl:choose>
<xsl:when test="value1">5</xsl:when>
<xsl:otherwise>25</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:inline padding-right="{$pad}mm">Value 1: <xsl:value-of select="value1"/></fo:inline>
Value 2: <xsl:value-of select="value2"/>
</fo:block>
<fo:block>
<xsl:variable name="pad">
<xsl:choose>
<xsl:when test="value3">5</xsl:when>
<xsl:otherwise>25</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:inline padding-right="{$pad}mm">Value 3: <xsl:value-of select="value3"/></fo:inline>
Value 4: <xsl:value-of select="value4"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Is there a simpler/better way to implement this?
Update:
I refactored the above into a template "field":
<xsl:template name="field">
<xsl:param name="txt"/>
<xsl:param name="val"/>
<xsl:param name="pad"/>
<xsl:variable name="p">
<xsl:choose>
<xsl:when test="$val">5</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$pad"><xsl:value-of select="$pad"/></xsl:when>
<xsl:otherwise>25</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:inline padding-right="{$p}mm"><xsl:value-of select="$txt"/>:</fo:inline>
<fo:inline keep-with-previous="always" padding-right="5mm" font-weight="bold"><xsl:value-of select="$val"/></fo:inline>
</xsl:template>
Which can be used like this:
<xsl:call-template name="field">
<xsl:with-param name="txt">Value 1</xsl:with-param>
<xsl:with-param name="val"><xsl:value-of select="sample/value1"/></xsl:with-param>
</xsl:call-template>
The template takes a third optional parameter, pad. If specified its value will be used as padding.
David's template (see accepted answer) uses a simpler if-contruct where the padding-right attribute is overwritten if needed.
Since this presumably isn't being executed in a browser is there any reason not to use xslt2 (which would make it rather nicer) since you are using java anyway the free saxon 9 implementation would work well here.
But here's an XSLT 1 version refactored a bit.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="sample">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="page-layout">
<fo:region-body margin="10mm" region-name="body"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="page-layout">
<fo:flow flow-name="body">
<xsl:call-template name="twoval">
<xsl:with-param name="a" select="value1"/>
<xsl:with-param name="ahead" select="'Value 1'"/>
<xsl:with-param name="b" select="value2"/>
<xsl:with-param name="bhead" select="'Value 2'"/>
</xsl:call-template>
<xsl:call-template name="twoval">
<xsl:with-param name="a" select="value3"/>
<xsl:with-param name="ahead" select="'Value 3'"/>
<xsl:with-param name="b" select="value4"/>
<xsl:with-param name="bhead" select="'Value 4'"/>
</xsl:call-template>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template name="twoval">
<xsl:param name="a"/>
<xsl:param name="ahead"/>
<xsl:param name="b"/>
<xsl:param name="bhead"/>
<fo:block>
<fo:inline padding-right="25mm">
<xsl:if test="$a"><xsl:attribute name="padding-right">5mm</xsl:attribute></xsl:if>
<xsl:value-of select="$ahead"/><xsl:text>: </xsl:text>
<xsl:value-of select="$a"/>
</fo:inline>
<xsl:text> </xsl:text>
<xsl:value-of select="$bhead"/><xsl:text>: </xsl:text>
<xsl:value-of select="$b"/>
</fo:block>
</xsl:template>
</xsl:stylesheet>