Search code examples
javaxmlapache-fopbarcode4j

Barcode4j + qr not working


I have problems trying to generate an image with qr code via barcode4j library. I've read the following guide but that didn't work out. So here is my code:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>fopqr</groupId>
    <artifactId>fopqr</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>Main</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>fop</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>net.sf.barcode4j</groupId>
            <artifactId>barcode4j</artifactId>
            <version>2.1</version>
        </dependency>

        <dependency>
            <groupId>net.sf.barcode4j</groupId>
            <artifactId>barcode4j-fop-ext</artifactId>
            <version>2.1</version>
        </dependency>

        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.2.0</version>
        </dependency>
    </dependencies>
</project>

2. Main.java

import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.xmlgraphics.util.MimeConstants;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;


public class Main {
    public static void main(String[] args) throws Exception{
        FopFactory fopFactory = FopFactory.newInstance();
        OutputStream out = new BufferedOutputStream(new FileOutputStream(new File("/home/user/fop.pdf")));

        try {
            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer(); // identity transformer
            Source src = new StreamSource(new File("/home/user/template.xsl"));
            Result res = new SAXResult(fop.getDefaultHandler());
            transformer.transform(src, res);
        }
        finally {
            out.close();
        }
    }
}

template.xsl

<?xml version="1.0" encoding="UTF-8"?>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="sans-serif" font-size="10pt">
  <fo:layout-master-set>
    <fo:simple-page-master master-name="A4" page-height="29.7cm" page-width="21cm" margin-top="2cm" margin-bottom="0.4cm" margin-left="2cm" margin-right="2cm">
      <fo:region-body margin-bottom="2.3cm"/>
      <fo:region-after extent="2.2cm"/>
    </fo:simple-page-master>
  </fo:layout-master-set>
  <fo:page-sequence master-reference="A4" language="en">
    <fo:flow flow-name="xsl-region-body">
                <fo:block>
                  <fo:instream-foreign-object>
                    <bc:barcode xmlns:bc="http://barcode4j.krysalis.org/ns" message="hello world">
                      <bc:qr/>
                    </bc:barcode>
                  </fo:instream-foreign-object>
                </fo:block>
                 </fo:flow>
  </fo:page-sequence>
</fo:root>

When I try to launch the application with

kirill@kirill:~/work/source/fop$ java -jar target/fopqr-1.0-SNAPSHOT-jar-with-dependencies.jar

I get

Mar 07, 2015 10:42:02 PM org.apache.fop.events.LoggingEventListener processEvent
WARNING: Unknown formatting object "{http://barcode4j.krysalis.org/ns}barcode" encountered (a child of fo:instream-foreign-object}. (See position 13:99)
Mar 07, 2015 10:42:02 PM org.apache.fop.events.LoggingEventListener processEvent
WARNING: Unknown formatting object "{http://barcode4j.krysalis.org/ns}qr" encountered (a child of barcode}. (See position 14:31)
Mar 07, 2015 10:42:02 PM org.apache.fop.events.LoggingEventListener processEvent
SEVERE: The intrinsic dimensions of an instream-foreign-object could not be determined. (See position 12:47)

What am I doing wrong?

UPDATE: I also want to add that I read FAQ: 4.1. The FOP extension fails. But as you can see I added all required libraries to my pom.xml file.


Solution

  • There are two issues:

    1. the 2.1.0 release of the barcode4j library does not contain the parts to create qr codes
    2. something goes wrong when starting things from an assembled jar "with dependencies embedded"

    For 2. I can only offer a workaround: add to your pom.xml in the build <plugins>:

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <configuration>
                    <mainClass>Main</mainClass>
                </configuration>
            </plugin>
    

    then you can run things with mvn exec:java and suddenly you should see a different error message:

    org.krysalis.barcode4j.BarcodeException: No barcode configuration element not found
    at org.krysalis.barcode4j.BarcodeUtil.createBarcodeGenerator(BarcodeUtil.java:110)
    at org.krysalis.barcode4j.BarcodeUtil.createBarcodeGenerator(BarcodeUtil.java:146)
    at org.krysalis.barcode4j.fop.BarcodeElement.getDimension(BarcodeElement.java:76)
    

    After a while I figured out the code wanted to tell me that there is nothing to render qr-codes. (e.g. replace your <bc:qr /> by <bc:code39/> and you should see the lib produces something (not a QR Code of course).

    So what to do? Build from source! Have your good old ant and cvs ready!

    cvs -z3 -d:pserver:[email protected]:/cvsroot/barcode4j co barcode4j
    cd barcode4j
    ant
    

    That should do it, except for telling maven. Sure there is a sane way to do it, but what worked for me is:

    cp ~/.m2/repository/net/sf/barcode4j/barcode4j/2.1/barcode4j-2.1.pom  pom.xml
    vi pom.xml  # change  <version>2.1</version> to <version>2.2-SNAPSHOT</version>
    mvn -Dfile=build/barcode4j.jar -DpomFile=pom.xml  install:install-file
    

    Now after having faked the jar into our maven repo, fix up the original pom.xml and update the dependencies (including a downgrade of zxing to 1.7, because the newer version is not compatible):

        <dependency>
            <groupId>net.sf.barcode4j</groupId>
            <artifactId>barcode4j</artifactId>
            <version>2.2-SNAPSHOT</version>
        </dependency>
    
        <dependency>
            <groupId>net.sf.barcode4j</groupId>
            <artifactId>barcode4j-fop-ext</artifactId>
            <version>2.1</version>
        </dependency>
    
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>1.7</version>
        </dependency>
    

    I guess it should have been better style to update the barcode4j-fop-ext to 2.2-SNAPSHOT too, but I leave this as an exercise for the reader. ;)

    Anyway, if I run mvn exec:java now, I get a fop.pdf with a QR-Code in it. (It does not look pretty, but some fiddling with the config like adding <bc:module-width>2mm</bc:module-width> or whatever sure fixes it).

    I have to admit that I prefer to leave it to someone else to figure out why running the same code from an assembled jar does not work.