Is there a way in XSL 1.0 to hand over variables or parameters using XSL fo:external-graphic like I would do when I'm using xsl:call-template
I know how I could work around the problem but I just wanted to know if there is a way I am not seeing.
If your SVGs are small enough, you could use fo:instream-foreign-object. (If the SVGs are very large, the size of the XSL-FO file might become a problem.)
main.xsl:
<xsl:import href="svg/svg_graphic.xsl" />
<xsl:template match="some/context">
<fo:instream-foreign-object>
<xsl:call-template name="make-svg">
<xsl:with-param name="param-a" select="..." />
</call-template>
</fo:instream-foreign-object>
</xsl:template>
svg_graphic.xsl:
<xsl:template name="make-svg">
<xsl:param name="param-a" select="..." />
<svg:svg>
...
</svg:svg>
</xsl:template>
<fo:external-graphic src="svg/svg_graphic.xsl" /> is not going to work. Inside your XSLT stylesheet, the elements in the XSL-FO namespace are just literal result elements. They are copied to the result tree, and they are not otherwise acted on by the XSLT processor. XSLT-specific attributes on literal result elements (where the attribute in the XSLT namespace and is defined as meaning something when used on literal result elements) are acted on by the XSLT processor. Attribute value templates ({...}) in attribute values of literal attributes (and some XSLT-defined attributes) are acted on by the XSLT processor.
There is no XSLT 1.0 way to get the XSLT processor to run another stylesheet based on the value of an XSL-FO-defined attribute.
There's also no XSLT 1.0 way to generate multiple result documents from one run of a stylesheet. Your XSLT processor probably has a processor-specific (or EXSLT) way to do that. The extension, if it exists, might not let you generate part of the XSL-FO result document, generate an SVG result document, and then go back to generating more of the XSL-FO document.
I created originally the following variable:
<xsl:variable as="document-node()" name="changesTexts">
<xsl:document>
<ps >
<p>Processed with <ptr target="#{$applicationID}"/>.</p>
<p>proofreading according to workflow 1.1.</p>
</ps>
</xsl:document>
</xsl:variable>
When I tried to access it like this
<xsl:variable name="p" select="$changesTexts//p"/>
It didn't work: an empty item() was the result.
After I added the namespace to the root element,
<ps xmlns="http://www.music-encoding.org/ns/mei">
I can access the desired elements by:
<xsl:variable name="p" select="$changesTexts//mei:p"/>
So basically I solved my problem but I would like to understand how the things work. I couldn't figure out what kind of default namespace the elements in the first case get. I tried:
name() (BTW this doesn't show me the namespace in the regular xml document either)
namespace-uri()
Also, I noticed in the debugger, that the variable $changesTexts is of the type document-node and in other cases, when I use fn:document(), the variables are of the type document-node(1). So there is obviously some subtle difference (?)
When you use a literal result element S in XSLT to create an element R in your result tree, the expanded name of R will be the same as the expanded name of S: that is, it will have the same local-name and the same namespace.
So the namespace of the elements constructed by your <ps> and <p> instructions is determined by the default namespace declared in the stylesheet (probably on the xsl:stylesheet element, but it could be on some inner element).
In a identity transform we can delete an attribute by
<xsl:template match="#myAttrib"/>
this works for any input... And we can "replace" the attribute by an element with
<xsl:template match="#myAttrib"><b>my new element</b></xsl:template>
... but it works only when input have only one attribute.
By other hand, if I need to replace attribute's value, the xsl:template behaviour is the same, that is,
<xsl:template match="#myAttrib">newValue</xsl:template>
not replaces the value, but delete attribute and include the "newValue" as a textNode.
Why "replace value" is invalid?
Why "replace by element" is not an error?
Why "replace by element" in a "two attributes (per element) context" is an error?
EDIT (for explain "two attributes"), suppose the input
<root>
<parent myAttrib1="1" myAttrib2="2">
<child myAttrib="1" myAttrib3="1"/>
</parent>
<sibling myAttrib0="1"/>
</root>
only the element sibling have one attribute.
It's difficult to answer your questions because some of your assumptions are wrong. For example:
<xsl:template match="#myAttrib"><b>my new element</b></xsl:template>
works for any number of elements. When applied (together with an identity transform template) to the following input:
<root>
<parent myAttrib="1">
<child myAttrib="1"/>
</parent>
<sibling myAttrib="1"/>
</root>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<parent>
<b>my new element</b>
<child>
<b>my new element</b>
</child>
</parent>
<sibling>
<b>my new element</b>
</sibling>
</root>
So clearly your assertion that "it works only when input have only one element" is not true.
With regard to:
<xsl:template match="#myAttrib">newValue</xsl:template>
This does not replace the value of myAttrib because the template matches the attribute - not its value (as an aside: the value of an attribute is not a node and cannot be matched). So just like before, the attribute is matched and another node is output in its place; first it was an element, now it's a text node. That's the only difference.
Edit:
The "replace procedure" is one attribute-node per one element-node.
No, that's not true either. Consider, for example, the following input:
<root>
<parent red="1" green="2">
<child red="1" blue="1"/>
</parent>
<sibling green="1" blue="1"/>
</root>
and the following template:
<xsl:template match="#red | #blue">
<new/>
</xsl:template>
or:
<xsl:template match="#*[contains(name(), 'r')]">
<new/>
</xsl:template>
--
BTW, none of these examples will work with Saxon - but that's another story.
Why "replace value" is invalid?
An XSLT template replaces one thing with another thing. In this case, you are replacing an attribute with a text node. If you want to replace an attribute with another attribute of the same name, but different content, you can do this:
<xsl:template match="#myAttrib">
<xsl:attribute name="{name()}">newValue</xsl:attribute>
</xsl:template>
Why "replace by element" is not an error?
See above. An XSLT template substitutes one thing for another.
Why "replace by element" in a "two attributes (per element) context" is an error?
This can cause an error in certain situations. It is not automatically an error.
XSLT does not allow adding attributes to a parent element in the output stream after other node types have already been added. Presumably, what is happening in your case is:
You are substituting myAttrib1 with an element.
You have an identity template copying myAttrib2 as a new attribute.
If myAttrib2 gets processed after myAttrib1, then an error will occur. (There is no guarantee on the order in which attributes will be processed).
This can be tricky to fix, but here is one approach that will work in certain cases:
<xsl:template match="#*[../#myAttrib]" />
<xsl:template match="#myAttrib">
<xsl:copy-of select="../#*[(. | current())[2]]" />
<b>my new element</b>
</xsl:template>
Translating to another readers what I understand from Michael's answer (#michael.hor257k) and my comments there; thanks Michael!
Assume you have an input XML,
<root>
<parent A="1" B="2">
<child C="1" D="1" E="0"/>
</parent>
<sibling E="1">text1</sibling>
</root>
Here's a diagram of the internal DOM representation, that is a tree:
root
/ \
parent sibling
/ \ \ \
(A,B) child (E) [text1]
\
(C,D,E)
The element root is a node, the element parent is a node, the attribute #A is a node, etc. Text also is a node... But not all the tree-itens are nodes: some itens in the diagram are into parenthesis, because they are collections of attribute-nodes.
In the diagram, the collection is a tree-item, the attribute not. We can imagine procedures to delete or replace items of the tree.
The "Delete node" task works with any individual node, pointed by its XPath.
We can imagine "Delete item" task as well (see diagram), and point the item by a XPath.
To delete a collection-item, the XPath must to point all the nodes of the collection, so parent/#* poits an item, but parent/#A not (because ramains parent/#B). XPath sibling/#E points a collection because sibling element have only one attribute. XPath #E points two nodes, one characterizing a collection, other not.
The task "Replace item X by text" or "Replace item X by element", need a XPath pointing the item X. Only tree-itens can be replaced. To replace a collection-item, the XPath must to point all the nodes of the collection.
Summarizing: the collection of attributes is the item, not the attribute-node; this is the point (!), and where confusion arises.
In DOM representation we can access nodeValue property, for both, elements and attributes, and we can change it in both cases: this is other source of confusion, because this concept of "change the nodeValue property" not exists in XSLT.
So,
Why "replace value" is invalid?
An XPath sibling/#E points to the a node attribute E. We need something like sibling/#E/nodeValue() to point the value and replace it, but this kind of XPath not exist.
(edit) IMPORTANT: as showed in this question, we can to change an attribute value in a ID-transform, by the use of xsl:attribute, see #JLRishe's answer in this page.
Why "replace by element" is not an error?
The concept is "replace an item by other item". Make sense when we see the diagram o the "tree of items".
Is wrong to imagine "replace a node by element" because is wrong to imagine a "tree of nodes", and a generic XPath node can be an attribute of a collection with more than one members.
Why "replace by element" in a "two attributes (per element) context" is an error?
Because the XPath of an individual attribute of a collection with more than one attributes, not represents the collection. The XPath must to point all attributes of the collection, to be used in a replace procedure.
I have an image (in the content) with a src attribute
http://myPage/rss.gif
I wan't to change the attribute. The xsl:template match expression doesn't work..
<replace css:content="#content" css:theme=".content" />
<xsl:template match="img/#src[contains(., 'rss.gif')]">
<xsl:attribute name="src">/++theme++myPackage/images/<xsl:value-of select="." /></xsl:attribute>
</xsl:template>
What am I doing wrong?
In your example you select an img-tag containing the string rss.gif in its src-attribute, this string is probably http://myPage/rss.gif.
Then you add an attribute src to the img-tag beeing its value the concatenation of the strings /++theme++myPackage/images/ and the original value http://myPage/rss.gif.
The original value is added by <xsl:value-of select="." /></xsl:attribute>. This selects the whole value of the src-attribute!
Thus you get something like:
<img id="content" src="/++theme++myPackage/images/http://myPage/rss.gif" />
For test purposes you could first add an img-element whith the expected src-attribute to your theme.html file and check if the image file is accessed. Please check also that the value of src in your content file only contains the string that you want to append to /++theme++myPackage/images/.
BTW, in your example you replace the theme element by class: css:theme=".content". Remind that this will replace all elements with the class content. Consider using id or a narrower selector e.g. div.content or better div#someid.content.
Is it possible to move the result generated by the XSLT processor. e.g. in the below case i wanted Benefit type="Main" and its child elements to be displayed before Benefit type="Rider"
Two separate templates are applied for Rider and Main, hence i think xsl:sort cannot be applied, since it sorts within a single collection.
<Policy>
<Benefit type="Rider">
<ProductAbbreviatedName>BBB</ProductAbbreviatedName>
<ProductCode>U30</ProductCode>
<ProductName>BBB</ProductName>
</Benefit>
<Benefit type="Main">
<ProductAbbreviatedName>AAA</ProductAbbreviatedName>
<ProductCode>231Y</ProductCode>
<ProductName>AAAA</ProductName>
</Benefit>
</Policy>
Kindly advice on some ideas to perform the desired output. Many thanks.
Just use in your code:
<xsl:apply-templates select="Benefit[#type='Main']"/>
<xsl:apply-templates select="Benefit[#type='Rider']"/>