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]">
Related
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.
I'm learning to use the mode attribute and must being doing something wrong. Apologies if this has been answered before but I'm not finding it here.
I want to process "title" elements separately depending on their context. For the main document, I want to add an "a" element inside of it:
<xsl:template match="title">
<div>
<xsl:value-of select="."/>
<a href="some_URL">some text
</a>
</div>
</xsl:template>
But elsewhere I'm creating result-documents where I just want the title:
<xsl:tamplate match="title" mode="print">
<div class="title">
<xsl:value-of select="."/>
</div>
</xsl:template>
In my main template match="/" I'm doing a for-each for each section, creating a result-document for each one:
<xsl:for-each select="/topic/body/bodydiv/section">
<xsl:result-document href="{$printoutfile}">
<html><head>some stuff</head><body>
<div class="section">
<xsl:apply-templates mode="print"/>
</div>
... more stuff...
</body</html>
</xsl:result-document>
</xsl:for-each>
Then I call everything else for the main document:
<html><head>stuff</head>
<body>
<div>
<xsl:apply-templates/>
</div>
</body>
</html>
The problem is this works for the result-document title but none of the rest of the result-document templates are used, since they don't have mode="print" on them. So the rest of the result-document all comes out as text.
Any idea what I need to do here? I'm obviously missing something basic.
thank you
Scott
You have not shown any of the other templates but if you expect the <xsl:apply-templates mode="print"/> to apply them then you need to have a mode="#all" on them. And if they do additional apply-templates then you need to use <xsl:apply-templates select="#current"/>. See http://www.w3.org/TR/xslt20/#modes for details.
I have a snippet:
<p>keyword1 text text more text
</p>
<p>more text</p>
<p>more text</p>
<p>keyword2 text text more text
</p>
<p>more text</p>
<p>more text</p>
<p>keyword3 text text more text
</p>
<p>more text</p>
<p>more text</p>
<p>keyword4
</p>
</body>
In the snippet above, I have a list of optional keywords. The text which follows is of variable length. There might be multiple groupings of <p></p> before the next keyword appears. When the next keyword appears, it signals the end of the previous keyword.
Whats a good way to go about doing this in XSLT.
edit:
suppose my keywords were: keyword1, keyword2, keyword3, keyword4
version 1.0
i'll post my xslt in a little while... its not working though
I'd use the XSLT 2.0 grouping constructs, with a group-starts-with attribute that returns true for each p element containing a keyword.
That is, something like this:
<xsl:variable name="keywords"
as="xs:string*"
select="('keyword1', 'keyword2', 'keyword3', 'keyword4')"
/>
<xsl:for-each-group select="p"
group-starting-with="tokenize(., '\s+') = $keywords">
<!--* process each group here ... *-->
</xsl:for-each-group>
it's not clear what kind of result you intend to get.
C.M. Sperberg's approach addresses the right basic idea, however the code provided seems not to run with my XSL processor. So I'd propose a transformation like this
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes" method="xml"/>
<xsl:variable name="keywords" select="'keyword1 keyword2 keyword3 keyword4'"/>
<xsl:template match="body">
<xsl:copy>
<xsl:for-each-group select="p" group-starting-with="p[contains($keywords,substring-before(.,' '))]">
<div>
<xsl:attribute name="class">
<xsl:value-of select="substring-before(current-group()[1],' ')"/>
</xsl:attribute>
<xsl:copy-of select="current-group()"/>
</div>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:transform>
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 the following XML
<title>
This is a <highlight>test</highlight> thanks.
</title>
and want to convert into
<span class="title">this is a <span class="highlight">test</span> thanks.</span>
I try this xslt, only can ge the text inside title tag, how can I also convert the highlight tag?
<span class="title"><xsl:value-of select="title"/></span>
<xsl:template match="title">
<span class="title">
<xsl:apply-templates />
</span>
</xsl:template>
<xsl:template match="highlight">
<span class="highlight">
<xsl:apply-templates />
</span>
</xsl:template>
or, if you want, collapse it into a single template:
<xsl:template match="title|highlight">
<span class="{name()}">
<xsl:apply-templates />
</span>
</xsl:template>
Key point is the <xsl:apply-templates /> - it runs all child nodes of the current node through the appropriate templates. In the upper variant, the appropriate templates are separate, in the lower variant the one template is called recursively.
There is a default rule defined in XSLT that copies text nodes. All text is run through this rule by <apply-templates>, so text nodes appear in the output autmatically.
Use xsl:copy-of instead of xsl:value-of if you want a full copy of the node and all of its children:
<span class="title"><xsl:copy-of select="title"/></span>
But for what you are trying to do, I would create one template for the title element and one for the highlight element:
<xsl:template match="title">
<span class="title"><xsl:value-of select="." /></span>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="highlight">
<span class="highlight"><xsl:value-of select="." /></span>
</xsl:template>