Search code examples
javaencodezxinggs1-datamatrix

How to handle FNC1/<GS> when encoding GS1 compatible DataMatrix code?


I get my DataMatrix code using the following line:

BitMatrix bitMatrix = new DataMatrixWriter().encode(dmie.preEncodeBarcode(dataToEncode), BarcodeFormat.DATA_MATRIX, 50, 50, null);

The input string I get contains "FNC1" (just as plain characters in the string) to mark the end of a dynamic field, and in total the string has 4 different GS1 DataMatrix Application Identifiers and their respective values.

What preEncodeBarcode() does is to replace "FNC1" with <GS>, like so:

input = input.replaceAll("FNC1", new String(new byte[] {0x1d}));

Since otherwise I simply get "FNC1910005FNC1230202[...]" encoded in the DataMatrix, while what I want is of course <GS> instead of the text "FNC1".

However, when replacing FNC1 with <GS> (I've tried using '\u001c' as well), I get this very strange double-DM-code instead of a normal one:

Strange-double-data-matrix

Only if I skip replacing "FNC1" with <GS> do I get a proper one.

Any idea how to get a proper DataMatrix code based on my <GS>-containing String? Or am I simply doing something wrong by having <GS> directly in the String? What should I do instead in that case to get zxing to give me a correct DataMatrix? I've been reading all over but I really can't wrap my head around this.

Update: I'm not sure, but I might be on to some strange sort of bug here. This is what I'm sending to the DataMatrixWriter once I've preprocessed the input string (spaces are ):

[d29100001 21000000049347037 24000163718 390300000002990

What I find rather obscure is that if I (at the time of writing) send input.substring(2, input.length()); or input.substring(0, input.length()-3); then it works just fine, while if I instead remove only one (or less) character from the beginning or 2 or less from the end then I get this strange DataMatrix. What's even stranger is that this behaviour is not even consistent - if I add say 6 some random numbers at the end then it works fine, but if I then remove three of those numbers then I again I get the problem. And worst of all, an hour ago I couldn't send in input.substring(0, input.length()-3), but now I can.

In other words, I'm utterly perplexed.

(PS. I am using the code found here to scale the DataMatrix to the size I want, but it's zxing that gives the wrong output from the start.)


Solution

  • Thanks to Lachezar Dobrev over at the zxing Google group I was able to get this working as expected. As it turns out, you can hand over an EncodeHintType to the DataMatrixWriter, which forces it to apply a square shape.

    This does the trick for me:

            HashMap hintMap = new HashMap();
            hintMap.put(EncodeHintType.DATA_MATRIX_SHAPE, SymbolShapeHint.FORCE_SQUARE);
            BitMatrix bitMatrix = new DataMatrixWriter().encode(input, BarcodeFormat.DATA_MATRIX, 50, 50, hintMap);
    

    Update some week later: As it turns out, zxing is not GS1 compatible no matter what input you give it, so the whole FNC1/ issue is besides the point. Instead, I would recommend using Okapi Barcode, which works flawlessly.

    I haven't found any guides or tutorials, but based on how the Okapi Java GUI generates its barcodes I puzzled this together from the project's Make Barcode class.

    DataMatrix dataMatrix = new DataMatrix();
    dataMatrix.setDataType(Symbol.DataType.GS1);
    dataMatrix.setReaderInit();
    dataMatrix.setPreferredSize(24); //144x144
    dataMatrix.forceSquare(true);
    dataMatrix.setContent(dataToEncode);
    BufferedImage image = new BufferedImage((dataMatrix.getWidth() * magnification) + (2 * borderSize),
                        (dataMatrix.getHeight() * magnification) + (2 * borderSize), BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = image.createGraphics();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setColor(Color.WHITE);
    g2d.fillRect(0, 0, (dataMatrix.getWidth() * magnification) + (2 * borderSize),
                        (dataMatrix.getHeight() * magnification) + (2 * borderSize));
    Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, borderSize, Color.WHITE, Color.BLACK);
    renderer.render(dataMatrix);
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        //Before Base64-encoding the image and return as a String        
        Base64.Encoder encoder = Base64.getEncoder(); 
        return encoder.encodeToString(baos.toByteArray());
    } catch (IOException e) {
    //Do some logging
    return "Something went wrong";//Return super-informative error message
    }