SVG to PDF converter that preserves text - pdf

I'm looking for a SVG to PDF converter that preserves the text in the SVG. I've tried Batik, Inkscape, and CairoSVG. The PDF generated by all of them is a bitmap image, including the text; the text cannot be selected/searched in a PDF viewer. All of them don't do a great job either, especially CairoSVG.
I followed the directions here (note that you don't have to compile FOP - you can download the PDF transcoder from here). Now I see that if I zoom into the PDF, the clarity is preserved, which I assume means the text is preserved. However, I cannot search or select the text.
Also, I compared the output of using PDF transcoder from FOP versus what's in Batik, and I see no difference.

If you're using filters, gradients or masking, it might be that it's impossible to translate this 1:1 to PDF. In these cases, converters usually raster the vector data to achieve a similar visual appearance instead of preserving the vector data and get a very different look.
Edit: In your example case, we can make sure that fill attributes are used instead of filters with the help of the following XSLT transformation:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#fill[ancestor::svg:symbol]" priority="1">
<xsl:attribute name="fill">currentColor</xsl:attribute>
</xsl:template>
<xsl:template match="#filter[starts-with(.,'url(#colorFilter-')]">
<xsl:attribute name="color">
<xsl:value-of select="concat('#',substring(.,18,6))"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="svg:use[not(#filter)]">
<xsl:copy>
<xsl:attribute name="color">#fff</xsl:attribute>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This fully relies on how in this particular SVG the filters are named, so it's not applicable to anything else. The colors aren't quite right, though. I'd be very interested in learning why this color matrix:
0.4 0 0 0 0
0 0.6 0 0 0
0 0 0.8 0 0
0 0 0 1 0
applied to white obviously does not result in rgba(40%,60%,80%,1).

Have a look at rsvg-convert, part of librsvg. I have used it to convert SVG documents to PDF and it preserves text such that it is selectable and searchable in PDF viewers.
Here is a blog post comparing it to some other options, and showing how to use it: https://www.itsfullofstars.de/tag/rsvg-convert/

HAve you tried printing the SVG to a PDF printer?

Related

xsl:fo Increment a variable inside page sequences?

I have a several pages sequences in my xsl file. An xsl-template is called inside each page sequence. Inside each template I have a block that contains a variable that I need to be incremented if the block is executed....I tried to use a global variable but I found in many posts here we cannot increment a global variable in xsl-fo...Can SomeOne please guides me How to do that ?
My xsl-file is something like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0"
xmlns:pdf="http://xmlgraphics.apache.org/fop/extensions/pdf">
<xsl:output encoding="UTF-8" indent="yes" method="xml"
standalone="no" omit-xml-declaration="no" />
<xsl:template match="analyseData">
<fo:page-sequence master-reference="simpleA6">
<fo:flow flow-name="xsl-region-body" border-collapse="collapse">
<xsl:call-template name="template1" />
</fo:flow>
</fo:page-sequence>
<fo:page-sequence master-reference="simpleA6">
<fo:flow flow-name="xsl-region-body" border-collapse="collapse">
<xsl:call-template name="template2" />
</fo:flow>
</fo:page-sequence>
<fo:page-sequence master-reference="simpleA6">
<fo:flow flow-name="xsl-region-body" border-collapse="collapse">
<xsl:call-template name="template3" />
</fo:flow>
</fo:page-sequence>
</xsl:template>
If you can come up with a pattern for all of the nodes in the source that need to be numbered and the numbering sequence is in the document order of the nodes in the source document, then you could use <xsl:number match="any" count="..." /> to do the counting. See https://www.w3.org/TR/xslt20/#numbering-based-on-position
If the count sequence doesn't match your source document or you can't find a pattern, then you're probably back to post-processing, as #kevin-brown suggests.
Well, there is very limited information here but I could guess.
Nothing stops you from using XSL and an identity-transform to modify some interim result.
So you could do what you are doing. Whenever you need to output this counter, why not write to the output <counter/>. Nothing more, just an empty tag that represents the counter.
Then write an identity-translate XSL that outputs the resulting file as is, except for a match on <counter>. This template would replace it with:
<fo:inline><xsl:value-of select="count(preceding::counter) + 1"/></fo:inline>
Note: you could maybe also use <xsl:number> here.
So you would do:
XML+XSL -> XSL FO with counters + XSL identity change counters -> XSL FO -> format with your formatter

XSLT: variables and "empty" labels

I have an XML datafile containing among other things a string of arbitrarily many comma separated values. I want those values to be displayed in a web browser as a list with one value per line. So I wrote an XSLT template that takes this string, displays the first value followed by a linebreak tag (<br/>), properly name-spaced, and resources with the remainder of the string. In effect, the commas are being replaced by HTML <br/> tags.
Now, when I store the result of calling that template in a xsl:variable, and display that through xsl:value-of, then the HTML tags disappear: what is shown is the string minus the commas.
When I display the result directly by having the xsl:call-template in place of the xsl:value-of, all is fine, and the values appear in a list.
So, what's going on?
Is this behavior an implementation artifact, or is it standard XSLT?
Use xsl:copy-of instead of xsl:value-of if you want to output nodes (like your br elements), xsl:value-of creates a simple text node with the string value(s) selected.
Here is an example that shows the difference between xsl:value-of and xsl:copy-of, you will note that it is not the use of the variable with newly created br elements that makes the difference, it is simply the use of xsl:value-of that creates a text() node with the string conversion of the selection:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html" indent="yes" version="5" doctype-system="about:legacy-doctype"/>
<xsl:variable name="var">Phrase 1.<br/>Phrase 2.<br/>Phrase 3.</xsl:variable>
<xsl:template match="/">
<html>
<head>
<title>.NET XSLT Fiddle Example</title>
</head>
<body>
<section>
<h1>Example 1: value-of</h1>
<xsl:value-of select="$var"/>
</section>
<section>
<h1>Example 2: copy-of</h1>
<xsl:copy-of select="$var"/>
</section>
<xsl:apply-templates select="//p"/>
<xsl:apply-templates select="//p" mode="copy-of"/>
</body>
</html>
</xsl:template>
<xsl:template match="p">
<section>
<h1>Example 1: value-of</h1>
<xsl:value-of select="."/>
</section>
</xsl:template>
<xsl:template match="p" mode="copy-of">
<section>
<h1>Example 1: copy-of</h1>
<xsl:copy-of select="."/>
</section>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/gWmuiJy/1
Output is
Example 1: value-of
Phrase 1.Phrase 2.Phrase 3.
Example 2: copy-of
Phrase 1.
Phrase 2.
Phrase 3.
Example 1: value-of
Line 1.Line 2.Line 3.
Example 1: copy-of
Line 1.
Line 2.
Line 3.
It seems that you hit the boundaries of the RTF ("Result tree fragment"):
When you use an XML fragment to initialize a variable or a parameter, then the variable or parameter is of the
"result tree fragment" datatype. This is an XSLT 1.0 specific datatype [just like node-set, but slightly different].
A result tree fragment is equivalent to a node-set that contains just the root node.
You cannot apply operators like "/", "//" or predicate on a result tree fragments. They are only applicable for node-set datatypes.
[...]
a) In XSLT 1.0
The resolution of this is to convert the result tree fragment into a node-set. I am not aware of any oracle specific xpath extension functions that can do this trick for you.
You could use EXSLT to achieve this.
b) Use XSLT 2.0
You can code your transformations in XSLT 2.0. XSLT 2.0 deprecates ResultTreeFragments i.e. if you are modeling an XSLT 2.0 transformation, and you create a variable or a parameter that holds a tree fragment, it is implicitly a node sequence.
So without using an XSLT version greater than 1, you're out of luck. So better use XSLT-2.0 or 3.0 to solve this problem.
Is this behavior an implementation artifact, or is it standard XSLT?
It is standard for XSLT-1.0, but not for XSLT-2.0+.

Variable declaration in nested tag in XSLT

Could anyone Please tell what is wrong with below code,
I am not able to create nested variable, i.e. in the format $v1/v2.
but i believe these format should work.
<xsl:variable name="n" select="100"/>
<xsl:variable name="v1">
<v2>
<xsl:value-of select="$n"></xsl:value-of>
</v2>
</xsl:variable>
<xsl:if test="$v1/v2">
<message:output>
<xsl:value-of select="$v1/v2"/>
</message:output>
</xsl:if>
In XSLT 2.0 your code looks fine; in XSLT 1.0 it would fail saying that in a path expression such as $v1/v2, the value of $v1 must be a node-set rather than a result-tree-fragment. Most XSLT 1.0 processors allow you to get around this restriction by using xx:node-set($v1)/v2 where xx is bound to some suiutable namespace.
The version of XSLT depends on what XSLT processor you are using. There are one or two processors that run XSLT 1.0 or 2.0 depending on what you ask for in the xsl:stylesheet version attribute, but a processor written in the XSLT 1.0 days doesn't know how to process XSLT 2.0, and most XSLT 2.0 processors if they see version="1.0" in the stylesheet will run XSLT 2.0 in "backwards compatibility mode", which doesn't impose all the restrictions of XSLT 1.0 (like the result-tree-fragment restriction), it merely makes some constructs behave the 1.0 way (for example, xsl:value-of will only output the first node in a node sequence).
It would be much easier to help you if you told us what your code output.
I am a bit confused here, when I checked the version using <xsl:value-of select="system-property('xsl:version')" /> it gave me 2 as output.
But in my stylesheet tag it was mentioned as 1.0, so below code was not working in that.
when i changed the version to 2.0: <xsl:stylesheet version="2.0"
the same code started working.
<xsl:template match="/">
<message:ExecuteSubProcessResponse>
<xsl:variable name="n" select="100"/>
<xsl:variable name="v1">
<v2>
<xsl:value-of select="$n"></xsl:value-of>
</v2>
</xsl:variable>
<xsl:choose>
<xsl:when test="count($v1/v2)> 0">
<message:BIMStatus>
<xsl:text disable-output-escaping="no">ELIGIBLE</xsl:text>
</message:BIMStatus>
</xsl:when>
<xsl:otherwise>
<message:BIMStatus>
<xsl:value-of select="$v1/v2"/>
</message:BIMStatus>
</xsl:otherwise>
</xsl:choose>
</message:ExecuteSubProcessResponse>
</xsl:template>

Can I optionally Include specific file extensions with Wix Heat using transforms?

Is it possible to include only certain file Extensions when using an xslt file to transform heat harvesting components using wix? I know I can exclude file extensions with the following:
<xsl:key name="exe-search" match="wix:Component[contains(wix:File/#Source, '.pdb')]" use="#Id" /> <!--.pdb-->
but is it possible to include several file types and exclude everything else (for example: .exe, .dll, .xml)?
Thank you!
Sure. Inside the condition, you can use full expressions, like:
contains(wix:File/#Source, '.pdb')
or contains(wix:File/#Source, '.exe')
or contains(wix:File/#Source, '.dll')
or contains(wix:File/#Source, '.xml')
If it's just an handful, that should be maintainable. To aid readability, you can put newlines inside the match attribute value (or any attribute in general).
I would use it like this. Notice the poor man's ends-with trick.
The template matches unwanted Components and replaces them with nothing.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wi="http://schemas.microsoft.com/wix/2006/wi"
xmlns="http://schemas.microsoft.com/wix/2006/wi">
<xsl:template match="wi:Component[not(
contains(concat(wi:File/#Source,'|'), '.exe|') or
contains(concat(wi:File/#Source,'|'), '.config|'))]">
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Here's a heat command that it works with:
heat dir "%wix%\bin" -cg CompGroup -ag -t byext.xsl -o test.wxs

Looping a variable length array with namespaces in XSLT

My previous question[1] is related to this. I found the answer for that. Now I want to loop a variable length array with namespaces. My array:
<ns:array xmlns:ns="http://www.example.org">
<value>755</value>
<value>5861</value>
<value>4328</value>
<value>2157</value>
<value>1666</value>
</ns:array>
My XSLT code:(have added the namespace in the root)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns1="http://www.example.org">
<xsl:template match="/">
<xsl:variable name="number" select="ns:array" />
<xsl:for-each select="$number">
<xsl:value-of select="$number" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
[1]https://stackoverflow.com/questions/20287219/looping-a-variable-length-array-in-xslt
IMHO you confused yourself by introducing a variable called number which actually contains a node set of value tags. Then, as a consequence you used your variable as singe item/node which does not yield the desired result (presumingly, since you did not really tell us what you want to do with the values).
Also, I think your question does not really have anything to with namespace issues as such. You just have to make sure that the namespaces in your select expressions match the namespaces in your input file.
I would suggest to do without the variable and change the way you retrieve the current value:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns1="http://www.example.org">
<xsl:template match="/">
<xsl:for-each select="ns:array">
<!-- Inside here you can work with the `value` tag as the _current node_.
There are two most likely ways to do this. -->
<!-- a) Copy the whole tag to the output: -->
<xsl:copy-of select="." />
<!-- or b1) Copy the text part contained in the tag to the output: -->
<xsl:value-of select="." />
<!-- If you want to be on the safe side with respect to white space
you can also use this b2). This would handle the case that your output
is required not to have any white space in it but your imput XML has
some. -->
<xsl:value-of select="normalize-space(.)" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>