I have trying to create pdf from html file using FOP.
My requirement is, I want to pass variable value at run time.
How can I pass variable value at run time?
It is not clear at what point you can inject "variables" nor how you expect to do them. Here's a sample that may provide some inspiration. It only uses a simple identity-translate and omits all the FO stuff for brevuty.
General principle -- put in a hidden div with some codes that are variables. For instance and simplicity, your input HTML now has this:
<html>
<div class="variables" style="display:none">
<div class="var_1" data-value="variable 1 value"/>
<div class="var_2" data-value="variable 2 value"/>
<div class="var_3" data-value="variable 3 value"/>
</div>
<div>
<div>Var 1 Value: <span class="variable" data-ref="var_1"/></div>
<div>Var 2 Value: <span class="variable" data-ref="var_2"/></div>
<div>Var 3 Value: <span class="variable" data-ref="var_3"/></div>
</div>
</html>
And you modify your XSL for a template that matches on a span where you want to insert the variable:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="span[#class='variable']">
<xsl:variable name="lookup">
<xsl:value-of select="#data-ref"/>
</xsl:variable>
<span>
<xsl:value-of select="//div[#class=$lookup]/#data-value"/>
</span>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The output of this would be:
<html>
<div class="variables" style="display:none">
<div class="var_1" data-value="variable 1 value"></div>
<div class="var_2" data-value="variable 2 value"></div>
<div class="var_3" data-value="variable 3 value"></div>
</div>
<div>
<div>Var 1 Value: <span>variable 1 value</span></div>
<div>Var 2 Value: <span>variable 2 value</span></div>
<div>Var 3 Value: <span>variable 3 value</span></div>
</div>
</html
Of course, you could expand that to include a template to strip the div whose class is variables for instance to not have it in the output or processed by your templates.
You can pass parameters to the XSLT stylesheet that generates the XSL-FO that is what FOP formats.
If you are using FOP to do the XSLT transformation, the format is -param name value (see https://xmlgraphics.apache.org/fop/2.3/running.html). If you are using an external XSLT processor to generate the XSL-FO that you pass to FOP, then you use the format that the XSLT processor requires (which will be specified in its documentation).
The closest things to variable text in the formatting stage are fo:marker and fo:table-marker, but even the markers are set before the formatting starts, and the variability comes from not knowing where page breaks occur until the document is formatted.
Related
I have scoured the forums and and still not clear on this. I'm very new to the topic.
I have HTML output that is being sent to a browser email client (Outlook). Outlook overwrites the characteristics of the <p> tag and introduces large spacing.
I would like to set up a template-match to replace all <p> tags with <div> or <span>.
For complicated reasons which will not be addressed in this post, I cannot stop the HTML from being rendered with <p> tags in it.
So lets say that I have:
<p xmlns="http://www.w3.org/1999/xhtml">
<span>Some text</span>
</p>
I would want the output to be
<span>Some text</span>
with the <p> tags removed.
If I have
<p xmlns="http://www.w3.org/1999/xhtml">
<b>Some other text</b>
</p>
then I would be happy with either:
<b>Some other text</b>
or
<span>
<b>Some other text</b>
</span>
Just as long as it gets rid of the <p> tags.
It would also need to recognize <p> without any attributes.
I thought about something like
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<span>
<xsl:apply-templates select="#*|node()" />
</span>
</xsl:template>
but this does not work. The <p> tags still appear.
It is possible to write an adapter which will intercept the HTML before it is sent to the smtp server and manipulate it, but there are considerable difficulties in this approach which I am looking to avoid.
Is it even possible to do what I am attempting? Any help greatly appreciated.
Your input documents, this sample for instance:
<p xmlns="http://www.w3.org/1999/xhtml">
<span>Some text</span>
</p>
Have a default namespace. And that's a good thing because a valid XHTML document must be in a namespace.
This means that this namespace applies to all elements in the document by default, and you have to account for this in your XSLT stylesheet. Redeclare this namespace there:
<xsl:stylesheet xmlns:xhtml="http://www.w3.org/1999/xhtml">
and whenever you make a reference to an element from the input document, prefix the element with xhtml::
<xsl:template match="xhtml:p">
<span>
<xsl:apply-templates select="#*|node()" />
</span>
</xsl:template>
This should get you started. You have not told us yet if the output document should also be in a namespace or not.
Currently, only modifying the two templates you already have, a structure like
<p>
<span/>
</p>
will end up as
<span>
<span/>
</span>
Is this acceptable for you? If not, there must be an additional rule (template) about p elements that contain span elements:
<xsl:template match="xhtml:p[xhtml:span]">
or perhaps
<xsl:template match="xhtml:p[//xhtml:span]">
I'm trying to parse a xml in which I need to Change div tag's attribute based on position() for the first one alone. As you can see below, I've already used a apply template for the parent div. How can I apply the condition for just the first div alone inside apply template?
<div class="maintitle">
<xsl:apply-templates select="title"/>
</div>
<xsl:template match="title">
<h3 class="sub title">
<span>
</span>
</h3>
<div class="slide">
<!-- ... html chunk ... -->
</div>
</xsl:template>
but for the first div in apply template should come as
<h3 class="sub title">
<span>
</span>
</h3>
<div class="first slide">
<!-- ... html chunk ... -->
</div>
Since you haven't shown what your input looks like, any answer will involve some guesswork.
But in general, there are two ways to make XSLT handle the first element of a series differently from others.
First, you can make two different templates, one for the first title element and one for the others:
<xsl:template match="title">
<!--* handling for most title elements *-->
</
<xsl:template match="title[1]">
<!--* handling for the first title *-->
</
In this case, that probably involves more duplication of the template code than one would like.
Second, you can use a conditional to surround the relevant code. Here, using an XSL attribute constructor instead of a literal result attribute will simplify things:
<xsl:template match="title">
<h3 class="sub title">
<span>
</span>
</h3>
<div>
<xsl:attribute name="class">
<xsl:if test="position() = 1">
<xsl:text>first</xsl:text>
</xsl:if>
<xsl:text> slide</xsl:text>
</xsl:attribute>
<!-- ... html chunk ... -->
</div>
</xsl:template>
These assume that:
each title element in your input turns into one slide in your output;
the title elements are siblings;
the title elements are not intermixed with elements of other types.
If these assumptions don't hold, the way you write the match patterns or the test conditions will differ from what's shown.
Is it possible to have out-of-sequence tags within XSLT using 1.0? My initial guess is not, as it breaks the rules of XML.
Consider XML data that has X elements, and I want to split those X entries into blocks of 3 within individual <div> blocks. What I would like to do is something this, but obviously it is completely invalid code...
<div>
<xsl:for-each select="mydata">
<xsl:value-of select="myvalue"/><br/>
<xsl:if test="(position() mod 3)=0">
</div> <!-- This is invalid -->
<div> <!-- This is invalid -->
</xsl:if>
</xsl:for-each>
</div>
So for 8 elements, the example result would be
<div>
value1<br/>
value2<br/>
value3<br/>
</div>
<div>
value4<br/>
value5<br/>
value6<br/>
</div>
<div>
value7<br/>
value8<br/>
</div>
If the above is simply not possible (as I suspect it is not), can somebody suggest an acceptable way to group them like this?
(Please note, this must be an XSLT 1.0 solution)
What you're trying to do is possible, but it isn't a good idea. This is a better approach:
<xsl:apply-templates select="mydata[position() mod 3 = 1]" mode="group" />
<!-- Separate templates -->
<xsl:template match="mydata" mode="group">
<div>
<xsl:apply-templates select=". | following-sibling::mydata[position() < 3]" />
</div>
</xsl:template>
<xsl:template match="mydata">
<xsl:value-of select="myvalue"/><br/>
</xsl:template>
JLRishe shows you the solution.
Your problem is that you are thinking of your stylesheet as writing start and end tags. That's not what XSLT does: it writes a tree. You can't write half a node to the result tree. Think nodes, not tags.
When you have problems like this in which the output structure doesn't exactly match the input structure, another useful rule of thumb is that the structure of the stylesheet should reflect the tree structure of the output, not that of the input. Don't think "what shall I do with the next ABC input node", but rather "I need to generate an XYZ node in the result tree, how shall I compute its content?".
I'm using Diazo/XSLT for theming a Plone website. On the homepage, Plone gives me the following structure:
<dl>
<dt>
The news item title
</dt>
<dd>
The content of the news item
</dd>
(and so on for the following ones)
</dl>
Which I want to transform into something like that:
<div id="news_items">
<div>
<h2>The news item title</h2>
The content of the news item.
<a class="readmore" href="<the HREF taken from the dt/a tag)">Read more</a>
</div>
(and so on for the following items)
</div>
I'm not really familiar with XSLT and Diazo (and more used to rewrite some pieces of existing themes) but I tried a few solutions.
The first one was to do this in two times. First look for each "dd" tags, creates the structure and after that update by parsing all "dt" tags:
<copy css:theme="#news_items">
<xsl:for-each css:select="#content-core dl dd">
<xsl:element name="div">
<xsl:element name="h2">
</xsl:element>
<xsl:copy-of select="./*" />
<xsl:element name="a">
<xsl:attribute name="class">readmore</xsl:attribute>
Read more
</xsl:element>
</xsl:element>
</xsl:for-each>
</copy>
It creates the structure correctly, but I don't know how to write the second part. The idea would be something like that:
<xsl:for-each css:select="#content-core dl dt">
<!-- Copy the link in the 'dd' tag into the h2 tag, based on the position -->
<copy css:theme="#news_item div:nth-child(position()) h2" css:select="a" />
<!-- Copy the a tag's href in the 'Read more' tag -->
<copy css:theme="#news_item div:nth-child(position()) a.readmore">
<xsl:attribute name="class">
<xsl:value-of select="#class" />
</xsl:attribute>
</copy>
</xsl:for-each>
I know it does not make that much sense, but I hope you get the idea of it:
I look into each "dd" tag
I find, based on the position in the loop, the 'h2' tag created in the previous step and I copy the link inside the "dd" tag.
I find (based on the position again) the "a.readmore" tag and copy the href.
The second solution I've been thinking about is a bit dirtier (and does not work either). The idea is to create the content in one step:
<xsl:for-each css:select="#content-core dl > *">
<xsl:if test="name = dt">
<!-- That the dt tag, we start the 'div' tag and add the h2 tag -->
</xsl:if>
<xsl:if test="name = dt">
<!-- That the dt tag, we copy the content and close the 'div' tag -->
</xsl:if>
</xsl:foreach>
But I really don't like the solution (and I don't see how to create the 'Read more' link).
Do you think the first solution can be done ? (especially the second step to populate the "h2" tag and add the "href" attribute on the "Read more" link).
Is there a way better solution available ?
I'd prefer to use only Diazo, but if it's not doable I can directly override the view in Plone (which would be simpler I think, at least I know how to do that)
Thanks for your help.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<div id="news_items">
<xsl:apply-templates select="dt"/>
</div>
</xsl:template>
<xsl:template match="dt">
<div>
<h2><xsl:copy-of select="a"/></h2>
<xsl:apply-templates select="following-sibling::dd[1]"/>
<a class="readmore" href="{a/#href}">Read more</a>
</div>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (with the same structure as the provided one but with more items):
<dl>
<dt>
The news item1 title
</dt>
<dd>
The content of the news item1
</dd>
<dt>
The news item2 title
</dt>
<dd>
The content of the news item2
</dd>
</dl>
produces the wanted, correct result:
<div id="news_items">
<div>
<h2>
The news item1 title
</h2>
The content of the news item1
<a class="readmore" href="someUrl1">Read more</a>
</div>
<div>
<h2>
The news item2 title
</h2>
The content of the news item2
<a class="readmore" href="someUrl2">Read more</a>
</div>
</div>
I have a HTML page (fully valid) that is post-processed by XSLT.
Let's say the relevant section of code looks like this:
<div id="content"> ... </div>
...
<div id="announcement"> ... </div>
The XSLT needs to transform it to look like this:
<div id="content"> <div id="announcement"> ... </div> ... </div>
Any ideas? I'm stuck.
Edit: Indicated that <div id="content"> and <div id="announcement"> are separated by other code.
If the announcement div directly follows the content div, use:
<xsl:template match="div[#id='content' and following-sibling::div[1][#id='announcement']">
<!-- copy the content div and its attributes -->
<xsl:copy>
<xsl:copy-of select="#*" />
<!-- now make a copy of the directly following announcement div -->
<xsl:copy-of select="following-sibling::div[1][#id='announcement']" />
<!-- process the rest of the contents -->
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- the empty template mutes an announcement div that follows a content div -->
<xsl:template match="div[#id='announcement' and preceding-sibling::div[1][#id='content']" />
The above is specific enough not to touch any other divs that might be in your document. If your situation allows, you can make it simpler/less specific to increase readability.