handling encoded elements - c#

My xslt transform does not pick up my document, this is because some/most elements are encoded,
e.g. my document:
<Template>
<ID>14</ID>
<Name>name of report</Name>
<VersionNumber>1.0</VersionNumber>
<CoverPage>
<br />
<br />
<h3 style="text-align: center;">
<br class="GENTICS_ephemera" />
</h3>
<h3 style="text-align: center;">
<br class="GENTICS_ephemera" />
</h3>
<h3 style="text-align: center;">Property Valuation Report</h3>
</CoverPage>
</Template>
This will never work:
<xsl:template match="span">
<fo:inline>
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
<xsl:template match="br">
<fo:block>
<fo:leader />
</fo:block>
</xsl:template>
Question: How can I get my document in the corrent format (technically my xml is valid and in correct format) but I want to do an xsl transform so I need to be able to pick up the correct tags
Here is the method for my transform:
private static MemoryStream Transform(XNode xmlData, XNode xslt)
{
XmlWriter writer = null;
var xslTrans = new XslCompiledTransform();
try
{
//load the xsl
xslTrans.Load(xslt.CreateReader());
//create the output stream
var result = new MemoryStream();
writer = XmlWriter.Create(result, null);
//create the xml reader for the data
var data = xmlData.CreateReader();
//do the actual transform of xml
xslTrans.Transform(data, null, writer);
writer.Close();
return new MemoryStream(result.ToArray());
}
catch (Exception e)
{
var errors = XslErrors.GetCompileErrors(xslTrans);
if (errors == null)
{
// Failed to obtain list of compile errors
throw;
}
if (writer != null) writer.Close();
throw e;
}
}
Update My XSLT Doc...
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0"
xmlns:fp="http://example.com/fp"
exclude-result-prefixes="fp">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" />
<xsl:variable name="pagewidth" select="21.5"/>
<xsl:variable name="bodywidth" select="17"/>
<xsl:template match="Template">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="Arial" font-size="9pt" color="black">
<fo:layout-master-set>
<fo:simple-page-master master-name="Cover" page-height="29.7cm" page-width="21cm" margin-top="2cm" margin-bottom="2cm" margin-left="2cm" margin-right="2cm">
<fo:region-body/>
</fo:simple-page-master>
<fo:simple-page-master master-name="BodyContent" page-height="29.7cm" page-width="21cm">
<fo:region-body margin-top="2cm" margin-bottom="2cm" margin-left="2cm" margin-right="2cm"/>
<!-- Header -->
<fo:region-before margin-bottom="2cm" extent="5cm" padding="0cm" border-width="0cm"/>
<!-- Footer -->
<fo:region-after margin-top="2cm" extent="2cm" padding="0cm" border-width="0cm"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="Cover">
<fo:flow flow-name="xsl-region-body" >
<fo:block id="CoverPageWrapper">
<xsl:apply-templates select="fp:ParseFragment(.)/node()"/>
<xsl:apply-templates select="CoverPage" />
</fo:block>
</fo:flow>
</fo:page-sequence>
<fo:page-sequence master-reference="BodyContent" initial-page-number="1">
<fo:static-content flow-name="xsl-region-before">
<fo:table margin-left="2cm" padding-top="1cm" table-layout="fixed" width="170mm">
<fo:table-column column-width="70mm" />
<fo:table-column column-width="100mm" />
<fo:table-body>
<fo:table-row>
<fo:table-cell padding-start="1pt" padding-end="1pt" padding-before="1pt" padding-after="1pt" padding-top="1pt" padding-bottom="1pt">
<fo:block-container width="3cm" height="2cm">
<fo:block>
</fo:block>
</fo:block-container>
</fo:table-cell>
<fo:table-cell padding-start="1pt" padding-end="1pt" padding-before="1pt" padding-after="1pt" padding-top="1pt" padding-bottom="1pt">
<fo:block-container width="4cm" height="2.2cm">
<fo:block>
</fo:block>
</fo:block-container>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:static-content>
<fo:static-content flow-name="xsl-region-after">
<fo:block text-align="center" font-size="8pt" padding-top="0.5cm">
Page
<fo:page-number/>
of
<fo:page-number-citation ref-id="FinalPage"/>
</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block id="BodyContentWrapper">
<xsl:apply-templates select="Body" />
</fo:block>
<!-- End of the document stuff that is needed-->
<fo:block id="FinalPage"/>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
<xsl:template match="label[#data-field-class='ui-templatefield']">
<xsl:choose>
<xsl:when test="#fo-checkbox" >
<xsl:choose>
<xsl:when test="text()='True'">
<fo:inline font-size="9pt" color="black">
[
<fo:inline font-family="ZapfDingbats" border-color="black" border-style="solid" border-width="1pt" font-size="6pt">✕</fo:inline>
]
</fo:inline>
</xsl:when>
<xsl:otherwise>
<fo:inline>
[   ]
<!-->fo:inline font-family="ZapfDingbats" font-size="10pt">❏</fo:inline-->
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<fo:inline>
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="span">
<fo:inline>
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
<xsl:template match="blockquote">
<fo:block
space-before="6pt" space-after="6pt"
start-indent="1em" end-indent="1em">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
<xsl:template match="h1">
<fo:block font-size="22pt">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="h2">
<fo:block font-size="18pt">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="h3">
<fo:block font-size="16pt">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="h4">
<fo:block font-size="14pt">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="h5">
<fo:block font-size="12pt" font-weight="bold">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="h6">
<fo:block font-size="10pt" font-weight="bold">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template name="tokenize-style">
<xsl:param name="pString" select="string(#style)"/>
<xsl:choose>
<xsl:when test="not($pString)"/>
<xsl:when test="contains($pString,';')">
<xsl:call-template name="tokenize-style">
<xsl:with-param name="pString"
select="substring-before($pString,';')"/>
</xsl:call-template>
<xsl:call-template name="tokenize-style">
<xsl:with-param name="pString"
select="substring-after($pString,';')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{normalize-space(substring-before($pString,':'))}">
<xsl:value-of select="normalize-space(substring-after($pString,':'))"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="div">
<fo:block>
<xsl:if test="#class='bordered'">
<xsl:attribute name="border-width">1pt</xsl:attribute>
<xsl:attribute name="border-style">solid</xsl:attribute>
</xsl:if>
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
<xsl:template match="p" >
<fo:block space-before="4pt" space-after="4pt">
<xsl:call-template name="tokenize-style"/>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
<xsl:template match="ol">
<fo:list-block start-indent="1cm" space-before="6pt" space-after="6pt">
<xsl:apply-templates/>
</fo:list-block>
</xsl:template>
<xsl:template match="ol/li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<fo:block>
<xsl:choose>
<xsl:when test="../#type != ''">
<xsl:number format="{../#type}"/>.
</xsl:when>
<xsl:otherwise>
<xsl:number format="1"/>.
</xsl:otherwise>
</xsl:choose>
</fo:block>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block font-size="11pt" color="black" font-family="Arial, Verdana, sans-serif">
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="ul">
<fo:list-block start-indent="1cm" space-before="6pt" space-after="6pt">
<xsl:apply-templates/>
</fo:list-block>
</xsl:template>
<xsl:template match="ul/li">
<fo:list-item>
<fo:list-item-label end-indent="label-end()">
<xsl:choose>
<xsl:when test="../#type ='disc'">
<fo:block>•</fo:block>
</xsl:when>
<xsl:when test="../#type='square'">
<fo:block font-family="ZapfDingbats">n</fo:block>
</xsl:when>
<xsl:when test="../#type='circle'">
<fo:block font-family="ZapfDingbats">m</fo:block>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="count(ancestor::ul) = 1">
<fo:block>•</fo:block>
</xsl:when>
<xsl:when test="count(ancestor::ul) = 2">
<fo:block font-family="ZapfDingbats">m</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block font-family="ZapfDingbats">n</fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</fo:list-item-label>
<fo:list-item-body start-indent="body-start()">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:list-item-body>
</fo:list-item>
</xsl:template>
<xsl:template match="dl">
<fo:block space-before="6pt" space-after="6pt">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="dt">
<fo:block>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="dd">
<fo:block start-indent="5mm">
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<xsl:template match="table">
<fo:table>
<xsl:attribute name="width">
<xsl:text>170mm</xsl:text>
</xsl:attribute>
<xsl:apply-templates select="*|text()"/>
</fo:table>
</xsl:template>
<xsl:template match="caption">
<fo:caption>
<fo:block>
<xsl:apply-templates />
</fo:block>
</fo:caption>
</xsl:template>
<xsl:template match="colgroup">
<xsl:for-each select="col">
<fo:table-column>
<xsl:attribute name="column-width">
<xsl:choose>
<xsl:when test="contains(#width, '%')">
<xsl:value-of disable-output-escaping="yes" select="floor(number(translate(#width,'%','')) div 100 * $bodywidth)"/>
<xsl:text>cm</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of disable-output-escaping="yes" select="floor(#width div 72)"/>
<xsl:text>in</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</fo:table-column>
</xsl:for-each>
</xsl:template>
<xsl:template match="tbody">
<fo:table-body>
<xsl:apply-templates select="*|text()"/>
</fo:table-body>
</xsl:template>
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-templates select="*|text()"/>
</fo:table-row>
</xsl:template>
<xsl:template match="th">
<fo:table-cell font-weight="bold" text-align="center">
<xsl:if test="ancestor::table/#border > 0">
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">0.1mm</xsl:attribute>
</xsl:if>
<fo:block>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="td">
<fo:table-cell padding-start="1pt" padding-end="1pt" padding-before="1pt" padding-after="1pt" padding-top="1pt" padding-bottom="1pt">
<xsl:if test="ancestor::table/#border > 0">
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">0.1mm</xsl:attribute>
</xsl:if>
<xsl:if test="ancestor::tr/#class='titleformat'">
<xsl:attribute name="background-color">black</xsl:attribute>
<xsl:attribute name="color">white</xsl:attribute>
<xsl:attribute name="font-size">9pt</xsl:attribute>
</xsl:if>
<fo:block>
<xsl:apply-templates select="*|text()"/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="tt">
<fo:inline font-family="monospace">
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
<xsl:template match="img">
<fo:external-graphic>
<xsl:attribute name="src">
file:<xsl:value-of
select="#src"/>
</xsl:attribute>
<xsl:attribute name="width">
<xsl:value-of
select="#width"/>px
</xsl:attribute>
<xsl:attribute name="height">
<xsl:value-of
select="#height"/>px
</xsl:attribute>
</fo:external-graphic>
</xsl:template>
<xsl:template match="pre">
<fo:block white-space-collapse="false">
<xsl:apply-templates select="*|text()"/>
</fo:block>
</xsl:template>
<xsl:template match="b">
<fo:inline>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
<xsl:template match="i">
<fo:inline>
<xsl:attribute name="font-style">italic</xsl:attribute>
<xsl:apply-templates select="*|text()"/>
</fo:inline>
</xsl:template>
<xsl:template match="hr">
<xsl:if test="#class='ui-pagebreak'">
<fo:block break-after="page" />
</xsl:if>
<xsl:if test="#class=''">
<fo:block>
<fo:leader
leader-pattern="rule" leader-length.optimum="100%"
rule-style="double" rule-thickness="1pt"/>
</fo:block>
</xsl:if>
</xsl:template>
<xsl:template match="br">
<fo:block>
<fo:leader />
</fo:block>
</xsl:template>
</xsl:stylesheet>

Here is an example with XslCompiledTransform and an extension object:
class Program
{
static void Main(string[] args)
{
XslCompiledTransform proc = new XslCompiledTransform();
proc.Load("../../XSLTFile1.xslt");
XsltArgumentList xsltArgs = new XsltArgumentList();
xsltArgs.AddExtensionObject("http://example.com/fp", new FragmentParser());
proc.Transform("../../XMLFile1.xml", xsltArgs, Console.Out);
}
}
public class FragmentParser
{
public IXPathNavigable ParseFragment(string fragment)
{
XmlDocument doc = new XmlDocument();
XmlDocumentFragment frag = doc.CreateDocumentFragment();
frag.InnerXml = fragment;
return frag;
}
}
then in your stylesheet you can use code as the following:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fp="http://example.com/fp"
exclude-result-prefixes="fp"
>
<xsl:template match="CoverPage">
<Test>
<xsl:apply-templates select="fp:ParseFragment(.)/node()"/>
</Test>
</xsl:template>
<xsl:template match="h3">
<heading>
<xsl:apply-templates/>
</heading>
</xsl:template>
</xsl:stylesheet>
[edit]
In your stylesheet you can change
<fo:page-sequence master-reference="Cover">
<fo:flow flow-name="xsl-region-body" >
<fo:block id="CoverPageWrapper">
<xsl:apply-templates select="fp:ParseFragment(.)/node()"/>
<xsl:apply-templates select="CoverPage" />
</fo:block>
</fo:flow>
</fo:page-sequence>
to
<fo:page-sequence master-reference="Cover">
<fo:flow flow-name="xsl-region-body" >
<fo:block id="CoverPageWrapper">
<xsl:apply-templates select="fp:ParseFragment(CoverPage)/node()"/>
</fo:block>
</fo:flow>
</fo:page-sequence>
Or add a template for CoverPage e.g.
<xsl:template match="CoverPage">
<xsl:apply-templates select="fp:ParseFragment(.)/node()"/>
</xsl:template>
and then above you would simply do e.g.
<fo:page-sequence master-reference="Cover">
<fo:flow flow-name="xsl-region-body" >
<fo:block id="CoverPageWrapper">
<xsl:apply-templates select="CoverPage" />
</fo:block>
</fo:flow>
</fo:page-sequence>

Related

Transforming xml using xslt with additional information

I'm currently working with a xsl style sheet that I found here and I am trying to add a certain improvement in order to render the time with the date .
When passed a particular date currently the output renders as <output>September 24th, 2020</output> but I would also like to include time something similar to below
September 24th, 2020 09:30
Any idea how I can make that improvement
The input is as :- 2020-09-24T09:30:00+00:00
My code is as follows
string xsltPath = # "D:\xslt\xslt.xml";
string xslt = File.ReadAllText(xsltPath);
var oldDocument = new XDocument(
new XElement("date", "2020-09-24T09:30:00+00:00")
);
var newDocument = new XDocument();
using(var stringReader = new StringReader(xslt)) {
using(XmlReader xsltReader = XmlReader.Create(stringReader)) {
var transformer = new XslCompiledTransform();
transformer.Load(xsltReader);
using(XmlReader oldDocumentReader = oldDocument.CreateReader()) {
using(XmlWriter newDocumentWriter = newDocument.CreateWriter()) {
transformer.Transform(oldDocumentReader, newDocumentWriter);
}
}
}
}
string result = newDocument.ToString();
Console.WriteLine(result);
The XSL style sheet is defined as below
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<output>
<xsl:variable name="month" select="substring(date, 6, 2)"/>
<xsl:choose>
<xsl:when test="$month=1">January</xsl:when>
<xsl:when test="$month=2">February</xsl:when>
<xsl:when test="$month=3">March</xsl:when>
<xsl:when test="$month=4">April</xsl:when>
<xsl:when test="$month=5">May</xsl:when>
<xsl:when test="$month=6">June</xsl:when>
<xsl:when test="$month=7">July</xsl:when>
<xsl:when test="$month=8">August</xsl:when>
<xsl:when test="$month=9">September</xsl:when>
<xsl:when test="$month=10">October</xsl:when>
<xsl:when test="$month=11">November</xsl:when>
<xsl:when test="$month=12">December</xsl:when>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:variable name="day" select="number(substring(date, 9, 2))"/>
<xsl:value-of select="$day"/>
<xsl:choose>
<xsl:when test="$day=1 or $day=21 or $day=31">st</xsl:when>
<xsl:when test="$day=2 or $day=22">nd</xsl:when>
<xsl:otherwise>th</xsl:otherwise>
</xsl:choose>
<xsl:text>, </xsl:text>
<xsl:value-of select="substring(date, 1, 4)"/>
</output>
</xsl:template>
</xsl:stylesheet>
You could extract the time from you date using a substring :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<output>
<xsl:variable name="month" select="substring(date, 6, 2)"/>
<xsl:choose>
<xsl:when test="$month=1">January</xsl:when>
<xsl:when test="$month=2">February</xsl:when>
<xsl:when test="$month=3">March</xsl:when>
<xsl:when test="$month=4">April</xsl:when>
<xsl:when test="$month=5">May</xsl:when>
<xsl:when test="$month=6">June</xsl:when>
<xsl:when test="$month=7">July</xsl:when>
<xsl:when test="$month=8">August</xsl:when>
<xsl:when test="$month=9">September</xsl:when>
<xsl:when test="$month=10">October</xsl:when>
<xsl:when test="$month=11">November</xsl:when>
<xsl:when test="$month=12">December</xsl:when>
</xsl:choose>
<xsl:text> </xsl:text>
<xsl:variable name="day" select="number(substring(date, 9, 2))"/>
<xsl:value-of select="$day"/>
<xsl:choose>
<xsl:when test="$day=1 or $day=21 or $day=31">st</xsl:when>
<xsl:when test="$day=2 or $day=22">nd</xsl:when>
<xsl:otherwise>th</xsl:otherwise>
</xsl:choose>
<xsl:text>, </xsl:text>
<xsl:value-of select="substring(date, 1, 4)"/>
<xsl:text> </xsl:text>
<xsl:value-of select="substring(date, 12, 5)"/>
</output>
</xsl:template>
</xsl:stylesheet>
See it working here : https://xsltfiddle.liberty-development.net/bEzknt7

c# XDocument : Check if particular node name exists , if not add

I have below node which needs to be added in xslt if not exists :-
<xsl:template name="URLSpliter">
<xsl:param name="url" />
<xsl:variable name="splitURL" select="substring-after($url, '/')" />
<xsl:if test="contains($splitURL, '/')">
<!--To call the template recursively-->
<xsl:call-template name="URLSpliter">
<xsl:with-param name="url" select="$splitURL" />
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($splitURL, '/'))">
<xsl:value-of select="$splitURL" />
</xsl:if>
</xsl:template>
For this, first I need to check if it exists or not ?-
I have checked it through -
IEnumerable<XElement> xElements = from xmlAuthor in doc.Descendants()
let xElement = xmlAuthor.Element("URLSpliter")
where xElement != null
select xmlAuthor;
var IsUrlSplitterExists= xElements.Any();
if(IsUrlSplitterExists)
{
}
1.I want to know if its correct way or not?
If not exists (element [name="URLSpliter"]) then needs to add.
How can I add it as a first node of xslt?
To select such elements in the XSLT namespace with LINQ to XML you would use
XNamespace xsl = "http://www.w3.org/1999/XSL/Transform";
if (!doc.Root.Elements(xsl + "template").Where(t => (string)t.Attribute("name") == "URLSplitter").Any()) {
doc.Root.AddFirst(new XElement(xsl + "template", new XAttribute("name", "URLSplitter"), ...))
}
Of course, as XSLT is XML, you might as well use XSLT to manipulate your XSLT:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:axsl="http://www.w3.org/1999/XSL/Transform-alias"
exclude-result-prefixes="axsl"
version="1.0">
<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
<xsl:output indent="yes"/>
<xsl:param name="new-template">
<axsl:template name="URLSpliter">
<axsl:param name="url" />
<axsl:variable name="splitURL" select="substring-after($url, '/')" />
<axsl:if test="contains($splitURL, '/')">
<!--To call the template recursively-->
<axsl:call-template name="URLSpliter">
<axsl:with-param name="url" select="$splitURL" />
</axsl:call-template>
</axsl:if>
<axsl:if test="not(contains($splitURL, '/'))">
<axsl:value-of select="$splitURL" />
</axsl:if>
</axsl:template>
</xsl:param>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xsl:transform[not(xsl:template[#name = 'URLSplitter'])] | xsl:stylesheet[not(xsl:template[#name = 'URLSplitter'])]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:copy-of select="$new-template"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94rmq5T

At least one of minimum, optimum, or maximum IPD must be specified on table

I'm getting this error (Title) when trying to execute a Render using Fo.Net when creating a PDF.
[MethodImpl(MethodImplOptions.Synchronized)]
public static void MakePdf(XmlDocument xslFoDocument, Stream outputStream)
{
FonetDriver driver = PdfPrinterDriver.InitFonetDriver();
driver.Render(xslFoDocument, outputStream);
}
I found another post on this subject (for Java rather than for C# as I'm using but i assume it is the same error with the same cause): At least one of minimum, optimum, or maximum IPD must be specified on table - XSL-FO Apache FOP. The problem is that I can't find anywhere in the xsl file below where size hasn't been set. Anyone know what might be wrong? Below is the full XSL stylesheet file
EDIT: After further Troubleshooting I managed to localize the table causing the exception to be thrown. I still can't see where I've missed declaring the size though.
<fo:table-column column-width="7cm"/>
<xsl:for-each select="$units">
<xsl:variable name="bgcolor">
<xsl:choose>
<xsl:when test="position() mod 2 = 0">white</xsl:when>
<xsl:otherwise>#F4F2F0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<fo:table-column background-color="{$bgcolor}"/>
</xsl:for-each>
<fo:table-body>
<fo:table-row>
<fo:table-cell>
<fo:block>
</fo:block>
</fo:table-cell>
<xsl:for-each select="$units">
<fo:table-cell text-align="center">
<xsl:choose>
<xsl:when test="not(contains($hiddenGroups, 'image'))">
<fo:block margin-top="3mm" margin-left="4mm" margin-right="4mm" margin-bottom="-0.8mm">
<fo:external-graphic content-width="500mm" width="100%" src="{$apiurl}ImageFiles/{Attributes/Attribute[#id='Image']/FileInfo/#id}/Data?apikey={$apikey}"/>
</fo:block>
<fo:block background-color="white" font-weight="600" padding-top="2mm" padding-bottom="2mm" margin-left="4mm" margin-right="4mm" margin-bottom="3mm">
<xsl:value-of select="#name"/>
</fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block background-color="white" font-weight="600" padding-top="2mm" padding-bottom="2mm" margin-top="3mm" margin-left="4mm" margin-right="4mm" margin-bottom="3mm">
<xsl:value-of select="#name"/>
</fo:block>
</xsl:otherwise>
</xsl:choose>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
<xsl:for-each select="$general_attributes">
<xsl:variable name="attribute_id" select="#id"/>
<xsl:variable name="attribute_type" select="#attributeDefinitionType"/>
<xsl:if test="not(contains(#id, 'HIDE_COMP'))">
<fo:table-row keep-with-previous="always" border-bottom-style="solid" border-bottom-width="thin" border-bottom-color="#D0D0D0">
<fo:table-cell display-align="center" padding-top="2mm" padding-bottom="2mm">
<fo:block margin-left="2mm" margin-right="2mm">
<xsl:value-of select="#name"/>:
</fo:block>
</fo:table-cell>
<xsl:for-each select="$units">
<fo:table-cell padding-top="2mm" padding-bottom="2mm" padding-left="4mm" padding-right="4mm" display-align="center" border-left-style="solid" border-right-style="solid" border-width="0.1mm" border-color="#D0D0D0">
<xsl:call-template name="show-attribute">
<xsl:with-param name="type" select="$attribute_type"/>
<xsl:with-param name="attribute" select="Attributes/Attribute[#id=$attribute_id]"/>
<xsl:with-param name="count" select="count($units)"/>
</xsl:call-template>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:if>
</xsl:for-each>
<xsl:if test="not(contains($hiddenGroups, 'Enkät'))">
<xsl:for-each select="$specific_attributes">
<xsl:variable name="attribute_id" select="#id"/>
<xsl:variable name="attribute_type" select="#attributeDefinitionType"/>
<xsl:if test="not(#group = preceding-sibling::*/#group)">
<fo:table-row>
<fo:table-cell padding-top="10mm" padding-bottom="5mm" number-columns-spanned="{1 + count($units)}">
<fo:block font-size="13" font-weight="bold" color="white" background-color="#0191ac" padding-top="2mm" padding-bottom="1.5mm" margin-bottom="2mm">
<fo:inline padding-left="4mm"><xsl:value-of select="#group"/></fo:inline>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:if>
<fo:table-row keep-with-previous="always" border-bottom-style="solid" border-bottom-width="thin" border-bottom-color="#D0D0D0">
<fo:table-cell display-align="center" padding-top="2mm" padding-bottom="2mm">
<fo:block margin-left="2mm" margin-right="2mm">
<xsl:value-of select="#name"/>
</fo:block>
</fo:table-cell>
<xsl:for-each select="$units">
<fo:table-cell padding-top="2mm" padding-bottom="2mm" padding-left="4mm" padding-right="4mm" display-align="center" border-left-style="solid" border-right-style="solid" border-width="0.1mm" border-color="#D0D0D0">
<xsl:call-template name="show-attribute">
<xsl:with-param name="type" select="$attribute_type"/>
<xsl:with-param name="attribute" select="Attributes/Attribute[#id=$attribute_id]"/>
<xsl:with-param name="count" select="count($units)"/>
</xsl:call-template>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>
</xsl:if>
</fo:table-body>
</fo:table>
I'm marking this as answered and reference to the first comment from #lfurini. As for the last question #lfurini, I was looking at the wrong table when I thought I'd set the table width already. So to sum up for anyone else also wondering about this; If the width is set in fo:table, it's enough to set the width on one of the columns and the rest will adjust.

Iterating in XSLT

I have the following XSL:
<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:param name='width' select ="270"/>
<xsl:param name='height' select="180"/>
<xsl:variable name="counter" select="0" />
<xsl:template name="while">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<line x1="{$counter}" y1="0.5" x2="{$counter}" y2="10.5" stroke="black" stroke-width="1" />
<xsl:variable name="counter" select="$counter + 10" />
<xsl:if test="$counter < $width">
<xsl:call-template name="while"/>
</xsl:if>
</svg>
</xsl:template>
</xsl:stylesheet>
I'm trying to get a line drawn every 10pixels across the width, like ruler markings.
When I run this code, it gets stuck in a loop. I can't debug, I just get a stack overflow exception. I presume either my counter value isn't increasing by 10, or that my evaluation of checking if the counter < width is incorrect.
Can someone please point me in the right direction?
I think you need to pass the params when calling your template.
Something like:
<xsl:template name="loop">
<xsl:param name="count" select="1"/>
<xsl:if test="$count > 0">
<xsl:call-template name="loop">
<xsl:with-param name="count" select="$count - 1"/>
</xsl:call-template>
<xsl:value-of select="$count"/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:template>
You need to pass the count to your template by using xsl:with-param.
Example:
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:output indent="yes"/>
<xsl:param name='width' select ="270"/>
<xsl:param name='height' select="180"/>
<xsl:template match="/">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<xsl:call-template name="while"/>
</svg>
</xsl:template>
<xsl:template name="while">
<xsl:param name="currentCount" select="0"/>
<line x1="{$currentCount}" y1="0.5" x2="{$currentCount}" y2="10.5" stroke="black" stroke-width="1" />
<xsl:variable name="counter" select="$currentCount + 10" />
<xsl:if test="$counter < $width">
<xsl:call-template name="while">
<xsl:with-param name="currentCount" select="$counter"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

C# FlowDocument to HTML conversion

Basically, I have a RichTextBox and I want to convert the formatted contents of it to HTML so it can be sent as an email.
The method I am currently using does not give any formatting at all:
string message = new TextRange(messageTextBox.Document.ContentStart,
messageTextBox.Document.ContentEnd).Text;
So I searched around and found this, however, it is over 5 years old and in the comments an MSFT user has commented saying that it is no longer supported - "This sample has been removed from our sample set and is no longer supported", and the HTML it generates is in an older format than modern HTML or XHTML which would be better to have.
Can anybody show me how I can convert the formatted contents of a RichTextBox to HTML?
(So when the email is sent it the recipient sees the email with formatting)
The general technique is to use a XamlWriter to convert the FlowDocument content to a stream of XML, and then to use an XSLT transform to convert the XML to HTML. That's not much of an answer, but that's because there's a huge range of possible HTML representations of any given FlowDocument.
This transform, for instance, converts every top-level Section to a div, every Paragraph to a p, and every Run to a span whose class tells you whether or not it's italicized, bold-faced, or underlined, or any combination of the above. It was useful for the purpose I wrote it for, but to call it a lossy transformation is an understatement:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl x">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="x:Section[not(parent::x:Section)]">
<div>
<xsl:apply-templates select="node()"/>
</div>
</xsl:template>
<xsl:template match="x:Section">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="x:Paragraph">
<p>
<xsl:apply-templates select="node()"/>
</p>
</xsl:template>
<xsl:template match="x:Run">
<xsl:variable name="class">
<xsl:if test="#FontStyle='Italic'">
<xsl:text>i </xsl:text>
</xsl:if>
<xsl:if test="#FontWeight='Bold'">
<xsl:text>b </xsl:text>
</xsl:if>
<xsl:if test="contains(#TextDecorations, 'Underline')">
<xsl:text>u </xsl:text>
</xsl:if>
</xsl:variable>
<span>
<xsl:if test="normalize-space($class) != ''">
<xsl:attribute name="class">
<xsl:value-of select="normalize-space($class)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
</span>
</xsl:template>
</xsl:stylesheet>
Here's a value converter I wrote to do the conversion - note that in order to use the value converter, you also have to hack around and implement a version of RichTextBox that exposes the content as a dependency property. Really this whole project was a pain.
public class FlowDocumentToHtmlConverter : IValueConverter
{
private static XslCompiledTransform ToHtmlTransform;
private static XslCompiledTransform ToXamlTransform;
public FlowDocumentToHtmlConverter()
{
if (ToHtmlTransform == null)
{
ToHtmlTransform = LoadTransformResource("Converters/FlowDocumentToXhtml.xslt");
}
if (ToXamlTransform == null)
{
ToXamlTransform = LoadTransformResource("Converters/XhtmlToFlowDocument.xslt");
}
}
private static XslCompiledTransform LoadTransformResource(string path)
{
Uri uri = new Uri(path, UriKind.Relative);
XmlReader xr = XmlReader.Create(Application.GetResourceStream(uri).Stream);
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(xr);
return xslt;
}
#region IValueConverter Members
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is FlowDocument))
{
return null;
}
if (targetType == typeof(FlowDocument))
{
return value;
}
if (targetType != typeof(string))
{
throw new InvalidOperationException(
"FlowDocumentToHtmlConverter can only convert back from a FlowDocument to a string.");
}
FlowDocument d = (FlowDocument)value;
using (MemoryStream ms = new MemoryStream())
{
// write XAML out to a MemoryStream
TextRange tr = new TextRange(
d.ContentStart,
d.ContentEnd);
tr.Save(ms, DataFormats.Xaml);
ms.Seek(0, SeekOrigin.Begin);
// transform the contents of the MemoryStream to HTML
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
XmlReader xr = XmlReader.Create(ms);
XmlWriter xw = XmlWriter.Create(sw, xws);
ToHtmlTransform.Transform(xr, xw);
}
return sb.ToString();
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
{
return new FlowDocument();
}
if (value is FlowDocument)
{
return value;
}
if (targetType != typeof(FlowDocument))
{
throw new InvalidOperationException(
"FlowDocumentToHtmlConverter can only convert to a FlowDocument.");
}
if (!(value is string))
{
throw new InvalidOperationException(
"FlowDocumentToHtmlConverter can only convert from a string or FlowDocument.");
}
string s = (string)value;
FlowDocument d;
using (MemoryStream ms = new MemoryStream())
using (StringReader sr = new StringReader(s))
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
using (XmlReader xr = XmlReader.Create(sr))
using (XmlWriter xw = XmlWriter.Create(ms, xws))
{
ToXamlTransform.Transform(xr, xw);
}
ms.Seek(0, SeekOrigin.Begin);
d = XamlReader.Load(ms) as FlowDocument;
}
XamlWriter.Save(d, Console.Out);
return d;
}
#endregion
}
Extended version of XSLT sheet above I thought might help. Not perfect but slightly more comprehensive. Extension of an extension of another answer referencing this one.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl x">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<!--<xsl:template match="x:Section[not(parent::x:Section)]">
<div>
<xsl:apply-templates select="node()"/>
</div>
</xsl:template>-->
<xsl:template match="x:Section[not(parent::x:Section)]">
<xsl:variable name="style">
<xsl:if test="#FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="#FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(#TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="#FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="#FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="#FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="#FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(#Foreground, 1, 1), substring(#Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="#Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<div>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</div>
</xsl:template>
<xsl:template match="x:Section">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="x:Paragraph">
<xsl:variable name="style">
<xsl:if test="#FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="#FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(#TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="#FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="#FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="#FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="#FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(#Foreground, 1, 1), substring(#Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="#Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<p>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</p>
</xsl:template>
<xsl:template match="x:Span">
<xsl:variable name="style">
<xsl:if test="#FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="#FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(#TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="#FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="#FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="#FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="#FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(#Foreground, 1, 1), substring(#Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="#Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<span>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</span>
</xsl:template>
<xsl:template match="x:Run">
<xsl:variable name="style">
<xsl:if test="#FontStyle='Italic'">
<xsl:text>font-style:italic;</xsl:text>
</xsl:if>
<xsl:if test="#FontWeight='Bold'">
<xsl:text>font-weight:bold;</xsl:text>
</xsl:if>
<xsl:if test="contains(#TextDecorations, 'Underline')">
<xsl:text>text-decoration:underline;</xsl:text>
</xsl:if>
<xsl:if test="#FontSize != ''">
<xsl:text>font-size:</xsl:text>
<xsl:value-of select="#FontSize" />
<xsl:text>pt;</xsl:text>
</xsl:if>
<xsl:if test="#FontFamily != ''">
<xsl:text>font-family:</xsl:text>
<xsl:value-of select="#FontFamily" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="concat(substring(#Foreground, 1, 1), substring(#Foreground, 4))" />
<xsl:text>;</xsl:text>
</xsl:if>
<xsl:if test="#Foreground-Color != ''">
<xsl:text>color:</xsl:text>
<xsl:value-of select="#Foreground-Color"/>
<xsl:text>;</xsl:text>
</xsl:if>
</xsl:variable>
<span>
<xsl:if test="normalize-space($style) != ''">
<xsl:attribute name="style">
<xsl:value-of select="normalize-space($style)"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="text()"/>
<xsl:apply-templates select="node()"/>
</span>
</xsl:template>
</xsl:stylesheet>
For those who is looking for solution for .Net Core (APS.Net Core) - nuget MarkupConverter did the trick for me. Add dependency reference
<PackageReference Include="MarkupConverter" Version="1.0.6" />
Then use it
MarkupConverter.MarkupConverter markupConverter = new MarkupConverter.MarkupConverter();
try
{
message = markupConverter.ConvertXamlToHtml(message);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Failed to parse flowdocument");
}

Categories