How to preserve processing instructions whilst producing XSL-FO in eXist-db? - xslt

Is there a way to make eXist to return processing instruction as it is? It seems it somehow ignores it in the output.
Processing instructions are very useful if I use XEP as my rendering engine, hence it would be great to be able to preserve them before the root of the XSL-FO document or immediately after its start.
If I have in the template:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:tei="http://www.tei-c.org/ns/1.0" version="2.0">
<xsl:template match="/">
<fo:root>
<?xep-pdf-page-layout two-columns-right?>
It simply returns:
<fo:root xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
I’ve tried to touch preserving of processing instructions with:
declare option exist:serialize "method=xhtml media-type=text/xml process-xsl-pi=yes"; in the prolog of my query but to no avail.
UPDATE I
The steps of my scenario are:
loading the document from the DB
passing it to the transform:transform() function
whilst calling the transform:transform() function, I pass to it one basic stylesheet, which includes other stylesheets
I need to add the processing instruction to a stylesheet for pages-masters, which covers the root of the FO document later used by the rendering engine. This stylesheet is included (<xsl:include/>) into the basic one (gathering all the stylesheets) and passed to the function.

With XSLT, to output a processing instruction use https://www.w3.org/TR/xslt/#creating-processing-instructions, that is <xsl:processing-instruction name="xep-pdf-page-layout">two-columns-right</xsl:processing-instruction> to have the pi <?xep-pdf-page-layout two-columns-right?> in the transformation result.

Related

document-uri not working in XSLT when transform is called from eXist-db

This transform called docUri.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" expand-text="yes">
<xsl:output method="text"/>
<xsl:template match="document-node()">{document-uri(.)}</xsl:template>
</xsl:stylesheet>
does not return the name of the xml file being processed when called from exist-db with this xQuery
xquery version "3.1";
transform:transform(doc("/db/apps/data/aDatabaseFile.xml"),
doc("/db/apps/docUri.xsl"),())
It should return "/db/apps/data/aDatabaseFile.xml"
Looks like there was a similar issue on MarkLogic XSLT doc(uri) or document(uri) function not resolving uri in context of content database?
When transform:transform is executed in eXist-db, the datafile is handed off to Saxon for transformation. At that point Saxon is unaware of the node context of the file. It therefore cannot report the "name" of the file.
If one wants the name (or any other data related to its node), then Saxon has to be told how to retrieve the document from the database. To do this one passes the full path to the document as a parameter to the transform:transform function's third argument ($parameters). This parameter is made available to Saxon inside the XSL stylesheet (matching on name) and it will return what you need
Parameter as third argument:
let $parameters :=
<parameters>
<param name="my_saxon_path" value="xmldb:exist://db/apps/data/aDatabaseFile.xml"/>
</parameters>
In your XSL stylesheet you reference the parameter as
<xsl:param name="my_saxon_path"/>
and then use the parameter as a regular node:
<xsl:template match="document-node()">{document-uri($my_saxon_path)}</xsl:template>
This is explained fully in the eXist-db documentation.
The side-effect of this is that you don't pass the document directly into transform:transform function if Saxon can access it directly through the supplied path. Balance these techniques is dependant on where your code is optimised and where you want the heavy lifting to be done: by eXist's engine, or Saxon's....

XInclude in XSLT Stylesheets (eXist-db)

Some of my stylesheets are a bit large and some of their parts are repeating. I would like to use XInclude for them—which would allow me to separate them aside the whole stylesheet. I can’t use xsl:import or xsl:include here because I need to inject them into the specific place for generating bookmarks and active links (for XSL-FO).
If I use:
<xi:include href="/db/apps/tested-bunny/resources/xsl-fo/common/bookmark-tree.xml/>
… the .fo file produced really includes the part. However, the part is untranslated, which means it is there as is in the source. The XSL-FO processor thus ignores it and the pdf result is without bookmarks.
As for the separated part—I saved it as a regular XML file with two namespaces declared in the root element:
<fo:bookmark-tree xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:if test="$head-level ge '1'">
...
If I try to include the same code snippet in a form of XSL stylesheet, it is the same—it is injected there properly but it does not add its functionality to the whole stylesheet, it is there still untranslated.
Is there any specific practice or limitation I am not aware of? How to do that properly?
For me, the working solution was not XInclude but xsl:include and calling the template at the proper time:
...
</fo:declarations>
<!-- Bookmarks from the external stylesheet -->
<xsl:call-template name="bookmark-tree"/>
<fo:page-sequence master-reference="title-page">
...
I created the proper stylesheet. The important thing was to set the root element to the current context:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
<xsl:template name="bookmark-tree" match=".">
<fo:bookmark-tree>
...
And of course, it was necessary to include the stylesheet into the one where I call the template:
<xsl:include href="common/bookmark-tree.xsl"/>
For now, I consider this question as answered.

xsl document test failing in IE8 and 9?

I have the following in an XLST document
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:str="http://xsltsl.org/string" xmlns:dt="http://xsltsl.org/date-time" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/2005/xpath-functions" xmlns:xdt="http://www.w3.org/2005/xpath-datatypes">
<xsl:if test="document('../folder/somefile.xml')">
<!-- do stuff -->
</xsl:if>
In IE8 and IE9 attempting to process this file results in a JavaScript error,"The system cannot locate the object specified" - and the transform bugs out.
I have read about a 'doc-available' function in XLST2.0, but attempting to use it just gives me an error that the function is not available.
I have seen this question: How check document is available in xsl? - but I'd prefer not to add processor dependent code, so I'm hoping I'm just missing something with 'doc-available'.
I think the XSLT 1.0 specification (and MSXML used by IE is an XSLT 1.0 processor) allows the XSLT processor do either "signal an error" or to continue by returning an empty node set if a document() call fails. Thus MSXML reporting an error is allowed and not a bug. You will need to make sure outside of XSLT that the file exists.

How do I get the current folder path within an XSLT file?

Is there a way to get the current folder path from within a XSLT file?
I need it to locate other XML and XSLT files. We have different customer folders and will need to successfully find the correct files.
You can send it into the style-sheet from outside using xsl:param. Then you need to determine what the current path is when invoking the from the outside ;)
In MSXSL on Windows, you can use a script extension like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://tempuri.org/msxsl"
>
<msxsl:script language="JScript" implements-prefix="user">
<![CDATA[
var fso = new ActiveXObject("Scripting.FileSystemObject");
function getCurrentPath(){
return fso.GetFolder(".").Path
}
]]>
</msxsl:script>
<xsl:template match="/">
<xsl:value-of select="user:getCurrentPath()"/>
</xsl:template>
</xsl:stylesheet>
Other XSL processors support similar methods to use external resources (scripting languages, function libraries etc.), so this is just an example.
Is there a way to get the current
folder path from within a xslt file?
Need it to locate other xml and xslt
files
No need for any extension functions or even parameters to do that!
Any relative URLs used in the href attribute of an <xsl:import> or <xsl:include>
instruction are resolved based on the URL of the current XSLT stylesheet -- it only needs to have an URL, which is vlearly stated as true in the question above. This is very convenient in importing/including XSLT stylesheets.
The document() function also will resolve a relative URL in a similar way, thus making any additional XML document accessible using anrelative URL.
Lastly, here is an example how this facilities are massively used in a big library of XSLT functions and templates (FXSL 2.x):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="xs xdt f"
>
<!--
This module contains the FXSL versions of the "standard" XPath functions
These are intended as convenience functions, so that they can be passed
as parameters to other functions (e.g. to f:zipWith())
or curried and passed as parameters (e.g. to f:map())
-->
<xsl:import href="func-curry.xsl"/>
<xsl:import href="func-compose-flist.xsl"/>
<xsl:import href="func-standardArithmeticXpathFunctions.xsl"/>
<xsl:import href="func-standardBooleanXpathFunctions.xsl"/>
<xsl:import href="func-standardStringXpathFunctions.xsl"/>
<xsl:import href="func-standardNodesXpathFunctions.xsl"/>
<xsl:import href="func-standardSequencesXpathFunctions.xsl"/>
<xsl:import href="func-standardAggregateXpathFunctions.xsl"/>
<xsl:import href="func-standardDateTimeXpathFunctions.xsl"/>
<xsl:import href="func-standardXSLTXpathFunctions.xsl"/>
<xsl:import href="func-standardAxisXpathFunctions.xsl"/>
</xsl:stylesheet>
This may work for your setup:
<xsl:value-of select="system-property('user.dir')"/>
For example,
<xsl:value-of select="document(concat(system-property('user.dir'),'/',filename,'.xml'))//title[1]"/>
no...
but you could maybe workaround the problem by using relative URLs and/or passing parameters into the stylesheet.
In most XSLT processor, you can add custom functions as extensions. For example here is Saxon's documentation how to do that.
Not AFAIK (though you could always pass it as a param to the transform), but I'm not clear why relative paths won't work for you here.

Merge multiple xslt stylesheets

I have a xslt stylesheet with multiple xsl:imports and I want to merge them all into the one xslt file.
It is a limitation of the system we are using where it passes around the xsl stylesheet as a string object stored in memory. This is transmitted to remote machine where it performs the transformation. Since it is not being loaded from disk the href links are broken, so we need to remove the xsl:imports from the stylesheet.
Are there any tools out there which can do this?
You can use an XSL stylesheet to merge your stylesheets. However, this is equivalent to using the xsl:include element, not xsl:import (as Azat Razetdinov has already pointed out). You can read up on the difference here.
Therefore you should first replace the xsl:import's with xsl:include's, resolve any conflicts and test whether you still get the correct results. After that, you could use the following stylesheet to merge your existing stylesheets into one. Just apply it to your master stylesheet:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="xsl:include">
<xsl:copy-of select="document(#href)/xsl:stylesheet/*"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The first template replaces all xsl:include's with the included stylesheets by using the document function, which reads in the file referenced in the href attribute. The second template is the identity transformation.
I've tested it with Xalan and it seems to work fine.
It is impossible to include imported stylsheets into the main file without breaking import precedence. For example, you define a top-level variable in an imported stylesheet and redefine it in the main file. If you merge two files into one, you’ll get two variables with the same name and import precedence, which will result in an error.
The workaround is two replace xsl:import’s with xsl:include’s and resolve any conflicts. After that you are safe to replace xsl:include instructions with the corresponding files’ contents, because that is what XSLT-processor does:
The inclusion works at the XML tree level. The resource located by the href attribute value is parsed as an XML document, and the children of the xsl:stylesheet element in this document replace the xsl:include element in the including document. The fact that template rules or definitions are included does not affect the way they are processed.
A Manual merge is probably going to be the best option.
The main consideration will probably be to make sure that the logic for matching templates works in the combined stylesheet.
Why would you want to? They're usually seperated for a reason afterall (often maintainability)
You could always write the merge yourself - read the XSL files in, select the template items you're interested in and write to a new master XSL file...
import multiple xsl in single xsl
<xsl:import href="FpML_FXOption_Trade_Template1.xsl"/>
<xsl:apply-imports/>
<calypso:keyword>
<calypso:name>DisplayOptionStyle</calypso:name>
<calypso:value>Vanilla</calypso:value>
</calypso:keyword>
<xsl:import href="FpML_FXOption_Trade_Template2.xsl"/>
<xsl:apply-imports/>