XSLT - Convert images (and pdf) to base64
P粉529245050
P粉529245050 2024-01-16 16:01:19
0
1
501

I used Apache FOP 2.8 to convert Apache FOP Intermediate Format (IF) files into HTML files with a self-written xslt stylesheet.

As an external library, I currently only have saxon12he installed.

Problem #1 (Image to base64)

In the source IF document, there is an image xml element as shown below:

<image xlink:href="files\Logo.png"/>

It would be easy to convert it to HTML and get a similar output

<img src="files\Logo.png"/>

When using the following template:

<xsl:template match="image">
    <xsl:variable name="file-path"><xsl:value-of select="@xlink:href"/></xsl:variable>
    <img src="{$file-path}"/>
</xsl:template>

The problem here is that the generated HTML-file cannot be "standalone"...meaning there must be files## in addition to HTML-file< code> # Directory containing Logo.png so that HTML-file finds the image path files\Logo.png < /p>But what I want to achieve is that

HTML files are "independent".

Is there a way to convert

Logo.png to Base64 , maybe with a simple function call like:

<xsl:template match="image">
    <xsl:variable name="file-path"><xsl:value-of select="@xlink:href"/></xsl:variable>
    <img src="to-base64($file-path)"/>
</xsl:template>

Create the following output:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA...."/>

Question #2 (pdf to base64)

Another tricky part is that in intermediate formats,

xlink:href can also generate .pdf files...

<image xlink:href="files\Table_1234.pdf"/>

It would be great if you could convert it to a Base64 image in the same way as above.

Or maybe there is another way to make the HTML document "standalone", but converting to base64 is the only idea I have so far.

Method 1 (Saxon Java extension function)

I try to create a Java extension function for Saxon 12 HE following this document

So I implemented an

ExtensionFunctionDefinition

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;

public class ImageToBase64 extends ExtensionFunctionDefinition {
    @Override
    public StructuredQName getFunctionQName() {
        return new StructuredQName("ext", "http://example.com/saxon-extension", "imageToBase64");
    }

    @Override
    public SequenceType[] getArgumentTypes() {
        return new SequenceType[]{SequenceType.SINGLE_STRING};
    }

    @Override
    public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
        return SequenceType.SINGLE_STRING;
    }

    @Override
    public ExtensionFunctionCall makeCallExpression() {
        return new ExtensionFunctionCall() {
            @Override
            public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
                var filePath = ((StringValue)arguments[0]).getStringValue();
                // open file and convert to base64 string
                var resultBase64 = "12345";
                return StringValue.makeStringValue(resultBase64);
            }
        };
    }
}

Because the documentation says: "

Classes that implement these extended functions must be registered in the configuration ", this can be " achieved by subclassing net.sf.saxon.Transform or net.sf .saxon.Query, override the method applyLocalOptions() so that it makes the appropriate call to config.registerExtensionFunction(); " I also added an extension net.sf.saxon.Transform: < /代码>

import net.sf.saxon.Transform;
import net.sf.saxon.trans.CommandLineOptions;

public class Configuration extends Transform {
    @Override
    protected void applyLocalOptions(CommandLineOptions options, net.sf.saxon.Configuration config) {
        config.registerExtensionFunction(new ImageToBase64());
        super.applyLocalOptions(options, config);
    }
}

When I build the artifact to get the

jar file (I'm using IntelliJ btw.) I just add the "compile output" so the jar ends up being 3kb .

Then I put the jar into the lib folder next to

saxon-he-12.2.jar of Apache FOP and added xmlns:ext="http://example.com/saxon-extension" to xsl:stylesheet.

But when I call now

<xsl:value-of select="ext:imageToBase64('my/file/path')"/>

I get the error

net.sf.saxon.trans.XPathException: The 1-argument function named Q{http://example.com/saxon-extension}imageToBase64() cannot be found

P粉529245050
P粉529245050

reply all(1)
P粉128563140

I did this with the help of @MartinHonnen who told me to create my own extension function.

So I created a new java program (important to use Java 8) and added two classes:

package ExtensionsPackage;

import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.ExtensionFunctionCall;
import net.sf.saxon.lib.ExtensionFunctionDefinition;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.value.SequenceType;
import net.sf.saxon.value.StringValue;

public class ImageToBase64 extends ExtensionFunctionDefinition {
    @Override
    public StructuredQName getFunctionQName() {
        return new StructuredQName("ext", "http://example.com/saxon-extension", "imageToBase64");
    }

    @Override
    public SequenceType[] getArgumentTypes() {
        return new SequenceType[]{SequenceType.SINGLE_STRING};
    }

    @Override
    public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
        return SequenceType.SINGLE_STRING;
    }

    @Override
    public ExtensionFunctionCall makeCallExpression() {
        return new ExtensionFunctionCall() {
            @Override
            public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
                String filePath = ((StringValue)arguments[0]).getStringValue();
                // open file and convert to base64 string
                String resultBase64 = "12345";
                return StringValue.makeStringValue(resultBase64);
            }
        };
    }
}

And based on this stackoverflow-entryanother classMyTransformerFactory:

package ExtensionsPackage;

import net.sf.saxon.Configuration;
import net.sf.saxon.TransformerFactoryImpl;
import net.sf.saxon.lib.ExtensionFunctionDefinition;

public class MyTransformerFactory extends TransformerFactoryImpl {
    public MyTransformerFactory() {
        super();
        ExtensionFunctionDefinition imageToBase64Function = new ImageToBase64();
        this.getProcessor().registerExtensionFunction(imageToBase64Function);
    }

    public MyTransformerFactory(Configuration config) {
        super(config);
        ExtensionFunctionDefinition imageToBase64Function = new ImageToBase64();
        this.getProcessor().registerExtensionFunction(imageToBase64Function);
    }
}

Now build a jar file and place it into the lib folder of the Apache FOP.

Then add set CUSTOMOPTS=-Djavax.xml.transform.TransformerFactory=ExtensionsPackage.MyTransformerFactory Go to fop.bat and add %CUSTOMOPTS% to :runFop.

Add the namespace to the stylesheet:

<xsl:stylesheet version="1.0" 
    xmlns:ext="http://example.com/saxon-extension">

and use it like this:

<xsl:value-of select="ext:imageToBase64('my/file/path')"/>

If fop.bat is now executed via the console xsl:value-of will provide 12345.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template