search

Home  >  Q&A  >  body text

XSLT - Convert images (and pdf) to base64

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粉529245050P粉529245050314 days ago464

reply all(1)I'll reply

  • P粉128563140

    P粉1285631402024-01-17 09:10:25

    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.

    reply
    0
  • Cancelreply