Docbook <bibliosource> gets lost in the transformation - xslt

I have the following structure
<informalfigure xmlns="http://docbook.org/ns/docbook">
<info>
<bibliosource>Photo: John Doe</bibliosource>
</info>
<mediaobject>
<imageobject>
<imagedata contentdepth="30mm" contentwidth="35mm" fileref="path/to/img.jpg"/>
</imageobject>
</mediaobject>
<caption>
<para>Here goes the caption for the image</para>
</caption>
</informalfigure>
<imagedata> and the <caption> get rendered but the <bibliosource> is gone after the transformation.
I'm using the docbook-xsl-1.77.0/xhtml/docbook.xsl for the transformation... I need some guidance on where/how to change the xslt so that <bibliosource> gets transformed properly.
Thanks!

Here is a simple solution. Add the following to your XHTML customization layer:
<xsl:template match="d:caption">
<div>
<xsl:apply-templates select="." mode="common.html.attributes"/>
<xsl:call-template name="id.attribute"/>
<xsl:if test="#align = 'right' or #align = 'left' or #align='center'">
<xsl:attribute name="align"><xsl:value-of select="#align"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</div>
<div><xsl:value-of select="../d:info/d:bibliosource"/></div> <!-- This line added -->
</xsl:template>
The above works with the namespace-aware XSLT stylesheets (docbook-xsl-ns).
If you use the non-namespace-aware stylesheets (docbook-xsl), the corresponding customization becomes:
<xsl:template match="caption">
<div>
<xsl:apply-templates select="." mode="common.html.attributes"/>
<xsl:call-template name="id.attribute"/>
<xsl:if test="#align = 'right' or #align = 'left' or #align='center'">
<xsl:attribute name="align"><xsl:value-of select="#align"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</div>
<div><xsl:value-of select="../blockinfo/bibliosource"/></div> <!-- This line added; note blockinfo -->
</xsl:template>
The text of the <bibliosource> element will appear in a separate <div> directly below the caption. The original template is in graphics.xsl.

Related

Inline element data not appearing where expected after XSL transform

With the following XML
<para>Refer to Table 3 and Figure <grphcref refid="apm00-02-02-000018" shownow="0">6</grphcref>
for the door dimensions and clearances.</para>
and this XSL:
<xsl:template match="prcitem">
<xsl:for-each select="para">
<p>
<xsl:value-of select="." />
<xsl:apply-templates select="./grphcref" />
</p>
</xsl:for-each>
<xsl:apply-templates select="grphcref" />
<xsl:apply-templates select="table" />
<xsl:apply-templates select="unlist" />
</xsl:template>
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
I get:
<p>Refer to Table 3 and Figure 6 for the door dimensions and clearances.
6 - apm00-02-02-000018
when I expected:
<p>Refer to Table 3 and Figure 6 - apm00-02-02-000018
for the door dimensions and clearances.
Can anyone offer guidance as to where I went wrong?
thx
If a para element is supposed to be transformed to a p element then in my view the "natural" way in XSLT is a template
<xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
If any other elements need special treatment add a template e.g.
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
Text nodes are copied through to the result by the built-in templates.
The input sample doesn't explain why you mix template matching and for-each and why or whether you need xsl:apply-templates with any particularly selected nodes; as long as the input order should be preserved a simple processing of all child nodes with <xsl:apply-templates/> should suffice.

How to test XSLT having multiple mode with XSpec?

Need to write XSpec test case to test the XSLT, in which multiple modes are used for transformation.
But with below test-case, the xspec only tests the output with default mode applied.
I wonder if there is a way to test the final output of the transformation.
<!-- input.xml -->
<body>
<div>
<p class="Title"><span>My first title</span></p>
<p class="BodyText"><span style="font-weight:bold">AAAAAAA</span><span>2 Jan 2020</span></p>
</div>
</body>
<!-- conv.xsl -->
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!-- default mode : adding text-align attribute where #class=Title -->
<xsl:template match="*[ancestor::body]">
<xsl:choose>
<xsl:when test="#class = 'Title'">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#* except #style"/>
<xsl:attribute name="text-align" select="'center'"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- bodytext mode : changing element name to <title> where p[#class=Title] -->
<xsl:template match="p[#class]" mode="bodytext">
<xsl:choose>
<xsl:when test="#class = 'Title'">
<title>
<xsl:copy-of select="#* except #class"/>
<xsl:apply-templates mode="bodytext"/>
</title>
</xsl:when>
<xsl:otherwise>
<para>
<xsl:apply-templates mode="bodytext"/>
</para>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="body">
<xsl:variable name="data">
<body>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</body>
</xsl:variable>
<xsl:apply-templates select="$data" mode="bodytext"/>
</xsl:template>
<xsl:template match="node() | #*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node() | #*" mode="#current"/>
</xsl:copy>
</xsl:template>
O\P for first <p>:
-- after default mode applied: <p class="Title" text-align="center">. [below xspec tests this o\p]
-- final: <title text-align="center">. [Want to test this o\p]
<!-- test.xspec -->
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec" stylesheet="conv.xsl">
<x:scenario label="XSS00001: Testing 'p[#class=Title]' converts to 'title'">
<x:context href="input.xml" select="/body/div[1]/p[1]"/>
<x:expect label="Testing 'p' converts to 'title'">
<title text-align="center">
<span>My first title</span>
</title>
</x:expect>
</x:scenario>
</x:description>
Any suggestion in this regard would be a great help. Thanks...
I don't think it is solely the use of the modes that doesn't give you the result you want. However, the way you have set up the modes in your XSLT, if you match on that /body/div[1]/p[1] in the XSpec test scenario, you will get the stylesheet applied to only that p element. And obviously for that p there is the match on *[ancestor::body] in the unnamed mode and processing stops in that mode as the other mode is never used from that template.
So you might need to make the body element the context and use a scenario like the following:
<x:scenario label="XSS00002: Testing 'p[#class=Title]' converts to 'title'">
<x:context>
<body>
<div>
<p class="Title">...</p>
<p class="BodyText">...</p>
</div>
</body>
</x:context>
<x:expect label="Testing 'p' converts to 'title'">
<body>
<div>
<title text-align="center">...</title>
<para>...</para>
</div>
</body>
</x:expect>
</x:scenario>
Martin is quite right.
Another way of writing would be:
<x:scenario label="When a document contains 'body//p[#class=Title]'">
<x:context href="input.xml" />
<x:expect label="'p' is converted to 'title[#text-align]'"
test="body/div/title">
<title text-align="center">
<span>My first title</span>
</title>
</x:expect>
</x:scenario>
that is,
Remove #select from x:context, because you and/or conv.xsl seem to assume the transformation to start always from the document node (/).
Add #test to x:expect, because you seem to be interested only in the title element in the transformation result.

How to Add Color To a Div in XSL when Color is in the XML document

I have an XSL 1.0 document with the following:
<div class="workgroup_title">
<xsl:value-of select="./#name"/>
</div>
I need to set the color for this element. The color is in the XML file
<abc.xyz.color>FF5733</abc.xyz.color>
To get it, I use this:
<xsl:value-of select="./abc.xyz.color"/>
What I would have liked to do is
<div class="workgroup_title" style="color:"#<xsl:value-of select="./abc.xyz.color"/>>
<xsl:value-of select="./#name"/>
</div>
That's not allowed, though.
Or:
<xsl:attribute style="color:">#<xsl:value-of select="./abc.xyz.color"/></xsl:attribute>
But color is not one of the attributes that can be set like that.
You can use attribute value templates to compute (parts of) the value of a literal result element: <div style="color: #{abc.xyz.color}">...</div>
The following templates should suffice your needs:
<xsl:template match="text()" /> <!-- Removes the text from the <abc.xyz.color>FF5733</abc.xyz.color> element -->
<xsl:template match="/*"> <!-- Copies the root element and its namespace -->
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="div[#class='workgroup_title']"> <!-- Applies the template to the <div> element -->
<xsl:copy>
<xsl:attribute name="style"><xsl:value-of select="concat('color: #',../abc.xyz.color,';')"/></xsl:attribute>
<xsl:copy-of select="node()|#*" />
</xsl:copy>
</xsl:template>
Its output is:
<root xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<div style="color: #FF5733;" class="workgroup_title">
<xsl:value-of select="./#name"/>
</div>
</root>

Umbraco library getMedia parent node property xslt

How can I get a property from the parent node of a media item using "umbraco.library:GetMedia"?
This allows me to get the current nodes "#nodeName"
<xsl:value-of select="umbraco.library:GetMedia(., 0)/#nodeName" />
I want to get the parent of the current nodes "#nodeName", I've tried the following but it doesn't work:
<xsl:value-of select="umbraco.library:GetMedia(., 0)/../#nodeName" />
Can anyone help me out?
Cheers, JV
With a call to GetMedia() and the #parentID attribute in a variable, I've got it working using the following:
<xsl:template match="/">
<!-- Do not call unless an image was picked -->
<xsl:apply-templates select="$currentPage/image[normalize-space()]" />
</xsl:template>
<xsl:template match="image">
<!-- Note: These WILL fail if no media is selected -->
<xsl:variable name="mediaNode" select="umbraco.library:GetMedia(., false())" />
<xsl:variable name="parentMedia" select="umbraco.library:GetMedia($mediaNode/#parentID, false())" />
<p>
Media: <xsl:value-of select="$mediaNode/#nodeName" />
</p>
<p>
Parent: <xsl:value-of select="$parentMedia/#nodeName" />
</p>
</xsl:template>

how to call a template to edit a tei stylesheet

I'm trying to automatically make epubs with a list of names after the body section.. In order to do this, I'm changing the tei stylesheet.
First, I tryed to insert this code in the "to.xsl" file, inside the "profiles/default/epub" folder:
<xsl:template match="tei:body">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
<xsl:element name="back" namespace="http://www.tei-c.org/ns/1.0">
<xsl:element name="div" namespace="http://www.tei-c.org/ns/1.0">
<xsl:for-each select="//tei:rs[#type='luogo']">
<xsl:element name="p" namespace="http://www.tei-c.org/ns/1.0">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
In this case the output shows the list of names before the body section.
Then I found the "bodyHook" template that you can see here, but it doesn't work (or I don't understand how to use it).
I tried to write something like this:
<xsl:param name="indiceNomi">
<back>
<div>
<xsl:for-each select="//tei:rs[#type='luogo']">
<p>
<xsl:value-of select="."/>
</p>
</xsl:for-each>
</div>
</back>
</xsl:param>
<xsl:template match="tei:body">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
<xsl:call-template name="bodyHook"/>
<xsl:with-param name="creaIndice" select="$indiceNomi"/>
</xsl:template>
But it is incorrect (seems that xsl:with-param can't be inside xsl:template, even if I saw some example like this).
So, if this is my input file, what kind of code I have to write?
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="file:/C:/Users/User/Desktop/prova2.xsl"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<teiHeader><fileDesc>
<titleStmt>
<title>AA</title>
</titleStmt>
<publicationStmt><p><!-- supply publication information --></p></publicationStmt>
<sourceDesc>
<bibl>AA</bibl>
</sourceDesc>
</fileDesc><profileDesc>
<langUsage>
<language ident="ita">AA</language>
<language ident="lat">AA</language>
</langUsage>
</profileDesc></teiHeader>
<text>
<body>
<div type="book" n="3" xml:id="L3">
<head>AA
</head>
<div type="capitolo" n="1" xml:id="L3-01">
<head>AA</head>
<p>AA
<pb n="200"/>textt<rs type="luogo">Genova</rs>texttex ttexttexttexttexttexttexttexttext<rs type="luogo">London</rs>exttextte<rs type="luogo">Paris</rs>
texttexttexttexttexttexttexttexttext<rs type="luogo">Tokyo</rs>xttexttexttexttexttexttexttext<rs type="luogo">New York</rs>
<rs type="luogo">Dublin</rs><rs type="luogo">Moscow</rs><rs type="luogo">Cairo</rs>texttexttexttexttexttexttexttexttexttexttexttexttexttexttext</p>
</div>
</div>
</body>
</text>
</TEI>
Thanks in advance, it will help me a lot to have some answer.
I think you've misunderstood somewhat how to customize the TEI XSLT behavior. You're not supposed to modify the predefined files. The top of this page gives a simple explanation. Basically the idea is that you would create your own XSLT file that imports the tei.xsl file, and add your own templates to your own XSLT file to customize the behavior. For example, to define a template to be inserted at the end of the <body> section, you would add something like this to your XSLT file:
<xsl:template name="bodyEndHook">
<back>
<div>
<xsl:for-each select="//tei:rs[#type='luogo']">
<p>
<xsl:value-of select="."/>
</p>
</xsl:for-each>
</div>
</back>
</xsl:template>
Presumably, the TEI transform automatically calls this template at the end of the <body>. The default behavior for bodyEndHook is to do nothing, but you can override that default behavior by adding your own templat.