XSL to filter duplicates [duplicate] - xslt

This question already has answers here:
XSLT Removing duplicates
(4 answers)
Closed 9 years ago.
I am trying generate a PDF using XSL. But I dont want duplicates(it should not be displayed back to back) , only for value=Started.
Below is the XSL snippet where I display the query result (string_1 column).
<xsl:for-each select="root/query1/row">
<fo:table-row height="0.9cm">
<xsl:if test="event_name = 'Started'">
<fo:table-cell border-style="solid" border-width="0.5pt"
number-columns-spanned="5">
<fo:block font-family="Courier" color="Blue" font-size="10pt" font-weight="normal" text-align="center">
<xsl:value-of select="string_1"/>
</fo:block>
</fo:table-cell>
</xsl:if>
</xsl:for-each>
Eg,
My query1 may give results like:
String_1
======
Started
In Progress
Complete
Started
Started
In Progress
In Progress
Complete
My PDF should be
=========
Started
In Progress
Complete
Started
In Progress
In Progress
Complete
Sorry if I didn't provide much information. I am new to XSL.

I don't know, how your input XML exactly looks like, but you should be able to adapt from this XML
<root>
<query1>
<row>
<string_1>Started</string_1>
</row>
<row>
<string_1>In Progress</string_1>
</row>
<row>
<string_1>In Progress</string_1>
</row>
<row>
<string_1>In Progress</string_1>
</row>
<row>
<string_1>Complete</string_1>
</row>
</query1>
</root>
and this XSLT:
<xsl:template match="/">
<xsl:for-each select="root/query1/row">
#> <xsl:if test="not(preceding-sibling::*[1]=string_1)">
<fo:table-row height="0.9cm">
<!-- <xsl:if test="event_name = 'Started'"> -->
<fo:table-cell border-style="solid" border-width="0.5pt" number-columns-spanned="5">
<fo:block font-family="Courier" color="Blue" font-size="10pt" font-weight="normal" text-align="center">
<xsl:value-of select="string_1" />
</fo:block>
</fo:table-cell>
<!-- </xsl:if> -->
</fo:table-row>
#> </xsl:if>
</xsl:for-each>
</xsl:template>
I have highlighted the important lines by #>. I have commented out one of the if-statements which seems to be there for doing different formatting on started nodes.
preceding-sibling::*[1] gives you the last processed node. It is compared to string_1 which is the current node. If it is different (not()), it should become part of the output.

Related

First row of a table on a new page

I have a requirement that the first row on that page of a table (not the header) is different from the rest. I can't figure out if it can be done with markers or if it's even possible with just FO.
I basically have a legend for a graphic that would be tagged something like:
<row>
<callout>1</callout>
<name>Cog</name>
<description>Super important for stuff</description>
</row>
<row>
<callout>2</callout>
<name>Sprocket</name>
<description>Not nearly as important for stuff</description>
</row>
<row>
<callout>3</callout>
<name>Sprigot</name>
<description>I'm not creative</description>
</row>
<row>
<callout>4</callout>
<name>Last One</name>
<description>Blah blah blah</description>
</row>
The table would output as (X is a count for the number of tables):
Callout
Name
Desc
X - 1
Cog
Super important for stuff
   - 2
Sprocket
Not nearly as important for stuff
   - 3
Sprigot
I'm not creative
page break
Callout
Name
Desc
X - 4
Last One
Blah blah blah
The count only shows up on the first row on that page, but may obviously show up multiple times if the table spans multiple pages. Is there any way to get it to output like that? I'm using XSL 2.0, Saxon 9, and Apache FOP 2.7.
<xsl:template match="table">
<fo:table>
<fo:table-column column-width="20%"/>
<fo:table-column column-width="30%"/>
<fo:table-column column-width="40%"/>
<fo:table-header>
<fo:table-row>
<fo:table-cell>
<fo:block>Callout</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Name</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>Desc</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-header>
<fo:table-body>
<xsl:apply-templates/>
</fo:table-body>
</fo:table>
</xsl:template>
<xsl:template match="row">
<fo:row>
<xsl:apply-templates/>
</fo:row>
</xsl:template>
<xsl:template match="callout">
<fo:table-cell>
<!-- The code needs to go here to get the count to appear only if this row is the first row on the page. -->
<fo:block>
<xsl:text> - </xsl:text>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="name">
<fo:table-cell>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>
<xsl:template match="description">
<fo:table-cell>
<fo:block>
<xsl:apply-templates/>
</fo:block>
</fo:table-cell>
</xsl:template>

Index mapping in XSLT-2.0

I have created index page using XSLT code. The contents name are shown to index page correctly but i want to map contents according to page number for specific title.
I am getting title with their description So I have named that block as id="TOC" and given as ref-id=”TOC” but the page number is not reflecting in my title.
Title:- Title names present in Index page.
Summary:- Title content associated with page.
The below is my XSLT-2.0 sample code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="content_name">
<fo:block space-after="7pt" space-after.conditionality="retain" line-height="1.147" font-family="Calibri" font-size="15pt" font-weight="bold" language="FR">
<fo:inline>
<fo:leader leader-length="0pt" />
<xsl:value-of select="title"/>
<fo:page-number-citation ref-id="TOC"/>
</fo:inline>
</fo:block>
</xsl:template>
<xsl:template match="pro_list">
<fo:block space-after="15pt" space-after.conditionality="retain" line-height="1.147" font-family="Calibri" font-size="15pt" font-weight="bold" text-decoration="underline" language="FR">
<fo:inline>
<fo:leader leader-length="0pt" />
<xsl:value-of select="title" id="TOC"/>
</fo:inline>
</fo:block>
<fo:block space-after="8pt" space-after.conditionality="retain" line-height="1.147" font-family="Calibri" font-size="15pt" language="FR">
<fo:inline>
<fo:leader leader-length="0pt"/>
<xsl:value-of select="summary" disable-output-escaping="yes" />
</fo:inline>
</fo:block>
</xsl:template>
</xsl:stylesheet>
This is the output:
Contents
Title1 page no.
Title2 page no.
Title3 page no.
Suppose title1 content is associated with page2 & similar for title2-3 & title3-4, then output looks like as follows
Contents
Title1 2
Tilte2 3
Title3 4
Is there any syntax or predefined function available to do index mapping?

XSL-FO 2.0: Prevent page-break inside table

I can't find a way to prevent a page break inside a table in the RTF output.
I've tried a lot of combinations of keep-together / keep-with-next but nothing worked for me. The actual version has a parent fo:block with the attribute keep-together.within-page="always" including the whole table.
The problem only occurs when a RTF is generated. The PDF is correct and no page break inside a table exists. The table has a header-row and 3 data-rows. In the RTF there is a page-break after the header-row and the first 2 data-rows. On the next page the header is repeated and the last data-row is generated.
It's very important that the tables don't include a page-break.
Here is the relevant XSLT-Stylesheet code:
<fo:block keep-together.within-page="always" >
<xsl:for-each select="block">
<xsl:call-template name="drawData"></xsl:call-template>
</xsl:for-each>
<fo:table text-align="center">
<xsl:for-each select="row[#type='declare'][1]/column">
<fo:table-column column-number="position()" border-style="solid" border-color="#000000" border-width="0.5pt">
<xsl:attribute name="column-width"><xsl:value-of select="#width"/></xsl:attribute>
</fo:table-column>
</xsl:for-each>
<xsl:if test="row[#type='header']">
<fo:table-header>
<fo:table-row keep-together.within-page="2" background-color="#0000FF" color="#FFFFFF">
<xsl:for-each select="row[#type='header'][1]/column/block">
<fo:table-cell border-style="solid" border-color="#000000" border-width="0.5pt">
<xsl:attribute name="number-columns-spanned">
<xsl:value-of select="count(../../../row[#type='declare']/column) div count(../../../row[#type='declare'])"/>
</xsl:attribute>
<xsl:call-template name="drawData"></xsl:call-template>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</fo:table-header>
</xsl:if>
<fo:table-body>
<xsl:for-each select="row[not(#type='header')]">
<fo:table-row keep-together.within-page="2">
<xsl:for-each select="column/block">
<fo:table-cell border-style="solid" border-color="#000000" border-width="0.5pt">
<xsl:call-template name="drawData"></xsl:call-template>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
A screenshot of the relevant table:
At the moment (FOP version 2.1), the RTF output has a few limitations compared to the PDF output; in particular, it does not support keep properties.
The linked page states that
RTF output is currently unmaintained
and keeps are
supported by the RTF library but not tied into the RTFHandler
so, while it is probably unlikely that this feature will be fixed in future versions without external help, it could be relatively easy to implement it (in which case it would be a good idea to submit a patch).

XSL for-each(-group) nested in for-each loop

We're aiming to print invoice lines grouped first by type of service being invocied and then by job description and constultant names, in this manner:
CONSULTANCY
Doing Actual Work
Gummo - 2014-03-03
Zeppo - 2014-02-24
Harpo - 2014-03-07
Snide Remarks
Groucho - 2014-02-24
Harp Playing
Harpo - 2014-02-28
EXPENSES INCURRED
Cigars
We're extracting this data from an xml file with a somewhat complex structure, namely the national Danish OIOUBL standard:
<Invoice>
(...)
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:Note>Bla bla</cbc:Note>
<cbc:InvoicedQuantity unitCode="EA">1.2500</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="DKK">1395.0000</cbc:LineExtensionAmount>
<cac:Delivery>
<cbc:ActualDeliveryDate>2014-02-24</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="DKK">348.75</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="DKK">1395.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="DKK">348.75</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID schemeAgencyID="320" schemeID="urn:oioubl:id:taxcategoryid-1.1">StandardRated</cbc:ID>
<cbc:Percent>25.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID schemeAgencyID="320" schemeID="urn:oioubl:id:taxschemeid-1.1">63</cbc:ID>
<cbc:Name>Moms</cbc:Name>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:Item>
<cbc:Description>Snide Remarks</cbc:Description>
<cbc:Name>CONSULTANCY</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID schemeAgencyID="9" schemeID="foo">Groucho</cbc:ID>
<cbc:ExtendedID>Timer</cbc:ExtendedID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeAgencyID="9" schemeID="foo">2014-02-24</cbc:ID>
</cac:StandardItemIdentification>
<cac:AdditionalItemIdentification>
<cbc:ID schemeAgencyID="9" schemeID="foo">10000</cbc:ID>
</cac:AdditionalItemIdentification>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="DKK">1116.0000</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="EA">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
(...)
<cac:InvoiceLine>
<cbc:ID>6</cbc:ID>
<cbc:InvoicedQuantity unitCode="EA">1.0000</cbc:InvoicedQuantity>
<cbc:LineExtensionAmount currencyID="DKK">1116.0000</cbc:LineExtensionAmount>
<cac:Delivery>
<cbc:ActualDeliveryDate>2014-03-06</cbc:ActualDeliveryDate>
</cac:Delivery>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="DKK">279.00</cbc:TaxAmount>
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="DKK">1116.00</cbc:TaxableAmount>
<cbc:TaxAmount currencyID="DKK">279.00</cbc:TaxAmount>
<cac:TaxCategory>
<cbc:ID schemeAgencyID="320" schemeID="urn:oioubl:id:taxcategoryid-1.1">StandardRated</cbc:ID>
<cbc:Percent>25.00</cbc:Percent>
<cac:TaxScheme>
<cbc:ID schemeAgencyID="320" schemeID="urn:oioubl:id:taxschemeid-1.1">63</cbc:ID>
<cbc:Name>Moms</cbc:Name>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:Item>
<cbc:Description>Cigars</cbc:Description>
<cbc:Name>EXPENSES INCURRED</cbc:Name>
<cac:SellersItemIdentification>
<cbc:ID schemeAgencyID="9" schemeID="foo"></cbc:ID>
<cbc:ExtendedID>Timer</cbc:ExtendedID>
</cac:SellersItemIdentification>
<cac:StandardItemIdentification>
<cbc:ID schemeAgencyID="9" schemeID="foo">2014-03-06</cbc:ID>
</cac:StandardItemIdentification>
<cac:AdditionalItemIdentification>
<cbc:ID schemeAgencyID="9" schemeID="foo">20000</cbc:ID>
</cac:AdditionalItemIdentification>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="DKK">1116.0000</cbc:PriceAmount>
<cbc:BaseQuantity unitCode="EA">1</cbc:BaseQuantity>
</cac:Price>
</cac:InvoiceLine>
</Invoice>
We start our XSL style sheet off by doing a simple for-each loop which groups invoice lines by cac:InvoiceLine/cac:Item/cac:AdditionalItemIdentification (the consultancy lines are labelled 10000 and the expenses lines are labelled 20000); we then need to ensure that each consultancy line is grouped first by job description (cac:InvoiceLine/cac:Item/cbc:Description). We've tried several iterations of further for-each loops, for-each-group'ing and solutions which used key. However, they all seem to fall back on XPath issues
<xsl:choose>
<xsl:when test="$layouttype = '1'">
<xsl:for-each select="cac:InvoiceLine/cac:Item/cac:AdditionalItemIdentification[cbc:ID='10000']">
<!-- Invoice headline - Printed once-->
<xsl:if test="number(position()) = 1">
<fo:table-row margin-left="0mm" margin-right="0mm" white-space="normal">
<!-- see if period is empty so colspan should be = 2 -->
<xsl:choose>
<xsl:when test="../cac:StandardItemIdentification/cbc:ID != 'n/a'">
<fo:table-cell display-align="after">
<fo:block margin-bottom="1mm" font-weight="bold">
<xsl:value-of select="../cbc:Name"/>
</fo:block>
</fo:table-cell>
</xsl:when>
<xsl:otherwise>
<fo:table-cell number-columns-spanned="2" display-align="after">
<fo:block margin-bottom="1mm" font-weight="bold">
<xsl:value-of select="../cbc:Name"/>
</fo:block>
</fo:table-cell>
</xsl:otherwise>
</xsl:choose>
</fo:table-row>
</xsl:if>
<!-- the actual invoice line -->
<!-- and this is where it gets tricky... -->
<xsl:for-each-group select="../cbc:Description" group-by="../cbc:Description">
<fo:table-row>
<fo:table-cell>
<fo:block><xsl:value-of select="../cbc:Description"/></fo:block>
</fo:table-cell>
</fo:table-row>
<xsl:for-each select="current-group()">
<fo:table-row>
<fo:table-cell>
<fo:block>
<xsl:value-of select="../cac:SellersItemIdentification/cbc:ID"/>
</fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</xsl:for-each-group>
</xsl:for-each>
<xsl:for-each select="cac:InvoiceLine/cac:Item/cac:AdditionalItemIdentification[cbc:ID='20000']">
<!-- then the same pattern is repeated -->
It's probably best to avoid repeated code where you can. It is not clear if you are only ever going to have "Consultancy" and "Expenses Incurred", but it may be safe to assume not. I am also going to assume you can have multiple Item elements per InvoiceLine (although the solution given still work if this is not the case).
Anyway, you would start off by grouping Item elements by their AdditionalItemIdentification. This would create your groups for "Consultancy" and "Expenses Incurred"
<xsl:for-each-group select="cac:InvoiceLine/cac:Item"
group-by="cac:AdditionalItemIdentification/cbc:ID">
Then, the title of each group would done by simply outputting the name
<xsl:value-of select="cbc:Name" />
Now, within the current group (of Item) elements, you then need to group by description
<xsl:for-each-group select="current-group()"
group-by="cbc:Description">
Finally, in this group (still of Item) you want to group by SellersItemIdentification, although you probably need an extra check because not all Item elements have them (in the case of "Expenses Incurred"
<xsl:for-each-group select="current-group()[cac:SellersItemIdentification/cbc:ID != '']"
group-by="cac:SellersItemIdentification/cbc:ID">
Outputing the current "seller" would them be a case of using the grouping-key, and other fields:
<xsl:value-of select="current-grouping-key()" />
<xsl:value-of select="cac:StandardItemIdentification/cbc:ID" />
As I don't know XSL-FO very well, the XSLT provided as an example will just output vague "table" and "row" elements, but it should give you the general idea.
Try this XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:cac="cac" xmlns:cbc="cbc" exclude-result-prefixes="cac cbc">
<xsl:output indent="yes"/>
<xsl:template match="Invoice">
<xsl:for-each-group select="cac:InvoiceLine/cac:Item" group-by="cac:AdditionalItemIdentification/cbc:ID">
<table>
<row><xsl:value-of select="cbc:Name" /></row>
<xsl:for-each-group select="current-group()" group-by="cbc:Description">
<row><xsl:value-of select="cbc:Description" /></row>
<xsl:for-each-group select="current-group()[cac:SellersItemIdentification/cbc:ID != '']" group-by="cac:SellersItemIdentification/cbc:ID">
<row>
<xsl:value-of select="current-grouping-key()" /> - <xsl:value-of select="cac:StandardItemIdentification/cbc:ID" />
</row>
</xsl:for-each-group>
</xsl:for-each-group>
</table>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
This should output the following (based on your provided sample, which only includes Groucho, and not poor old Zeppo or Harpo)
<table>
<row>CONSULTANCY</row>
<row>Snide Remarks</row>
<row>Groucho - 2014-02-24</row>
</table>
<table>
<row>EXPENSES INCURRED</row>
<row>Cigars</row>
</table>

How to insert line breaks in a PDF generated with XSL-FO

I am generating a PDF using XSL-FO and XML. In a textbox, the user can enter data like "1", then he presses ENTER, then "2", ENTER, "3", etc. But in the XML and hence in the PDF, the output is "1234567". How can I preserve the line breaks? I already tried white-space-collapse, linefeed-treatment and white-space-treatment but that didn't help.
My XSL looks like:
<xsl:template match="AddCmt">
<fo:block keep-together="always"> Additional Comments
<fo:block-container border-style="solid" height="20mm" width="170mm" space-after="5mm">
<fo:block>
<xsl:attribute name="id">
<xsl:value-of select="../CMT_ID"/>
</xsl:attribute>
<xsl:value-of select="../ANS_CMT"/>
</fo:block>
</fo:block-container>
</fo:block>
</xsl:template>
When I enter the following:
hello
medhavi
saraswat
This is the XML I get:
<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type='text/xsl' href='e:\tmm-09.3\src\pmod\WorkOrder.xsl'?>
<Root>
<WorkOrders>
<Detail>Id="ANS_436‌​_FLD_1" Label="qq">qq</Detail>
<Ans Checked="0" Id="ANS_436_FLD_2" Label="ww">ww</Ans>
<ID>ANS_436_FLD</ID>
<ANS_FLD>0|0</ANS_FLD>
<CMT_ID>ANS_436_CMT‌​</CMT_ID>
<ANS_CMT>hello medhavi saraswat</ANS_CMT>
<Warning>
<Line>warning 11</Line>
<Line>22</Line>
<Line>33</Line>
<Line>44</Line>
<Line></Line>
<Line>66</Lin‌​e>
<Line>77</Line>
<Line></Line>
</Warning>
It should work with the following xml (you should add all the attributes):
<xsl:template match="AddCmt">
<fo:block keep-together="always"> Additional Comments
<fo:block-container border-style="solid" height="20mm" width="170mm" space-after="5mm">
<fo:block wrap-option="wrap" linefeed-treatment="preserve" white-space-collapse="false" white-space-treatment="preserve">
<xsl:attribute name="id">
<xsl:value-of select="../CMT_ID"/>
</xsl:attribute>
<xsl:value-of select="../ANS_CMT"/>
</fo:block>
</fo:block-container>
</fo:block>
</xsl:template>
But as I mentioned in the comments, if your XML already has no linebreaks, there's no way your PDF will. You mentioned in your question there are no linebreaks in your XML, hence no linebreaks in the PDF.
Try checking out why there are no linebreaks in the XML. If you can provide any more information (a piece of your XML, the code you use to construct the XML, ...), please edit your answer and add the information.