BarcodeWriter.java

/*
 * Copyright (C) 2019 sw4j.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.sw4j.tool.barcode.random.generator;

import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sw4j.tool.barcode.random.codedata.CodeData;
import org.sw4j.tool.barcode.random.config.CodeConfig;
import org.sw4j.tool.barcode.random.config.CodeType;
import org.sw4j.tool.barcode.random.config.EncodingConfig;

/**
 * <p>
 * This is a {@link java.util.function.Consumer Consumer} for
 * {@link org.sw4j.tool.barcode.random.generator.RandomIdent RandomIdent} which generates one barcode for a
 * {@code RandomIdent}.
 * </p>
 * <p>
 * This class is thread safe, as long as no barcodes for the same ident are generated.
 * </p>
 * @author Uwe Plonus &lt;u.plonus@gmail.com&gt;
 */
public class BarcodeWriter implements Consumer<IdentValue> {

    /**
     * <p>
     * The logger of this class.
     * </p>
     */
    private final Logger logger = Logger.getLogger(BarcodeWriter.class.getName());

    /**
     * <p>
     * The configuration for the barcode that should me generated.
     * </p>
     */
    private final CodeConfig codeConfig;

    /**
     * <p>
     * The factories for the input and output stream.
     * </p>
     */
    private final CodeData codeData;

    /**
     * <p>
     * Create a new instance with the given barcode config and output factories.
     * </p>
     * @param codeConfig the configuration for the barcode to generate.
     * @param codeData the factories for the output files.
     */
    public BarcodeWriter(final CodeConfig codeConfig, final CodeData codeData) {
        this.codeConfig = codeConfig;
        this.codeData = codeData;
    }

    /**
     * <p>
     * Generate the barcode for the given ident and configured barcode.
     * </p>
     * @param identValue the ident (and its values) for output.
     */
    @Override
    public void accept(final IdentValue identValue) {
        CodeType codeType = codeConfig.getType();
        EncodingConfig codeEncoding = codeConfig.getEncoding();
        String codeUrl = codeConfig.getUrl();
        if (codeUrl == null) {
            codeUrl = "{code}";
        }
        codeUrl = codeUrl.replace("{code}", identValue.getEncoded(codeEncoding));
        try {
            OutputStream os = codeData.getOutputForIdent(codeType, codeEncoding, identValue.getIdent(),
                    codeConfig.getFiletype());
            MultiFormatWriter codeWriter = new MultiFormatWriter();
            Map<EncodeHintType, Object> encodingParameters = new HashMap<>();
            encodingParameters.put(EncodeHintType.CHARACTER_SET, "utf-8");
            setErrorCorrection(encodingParameters);
            BitMatrix matrix = codeWriter.encode(codeUrl, codeType.getFormat(), codeConfig.getWidth(),
                    codeConfig.getHeight(), encodingParameters);
            MatrixToImageWriter.writeToStream(matrix, codeConfig.getFiletype(), os);
        } catch (IOException | WriterException exc) {
            logger.log(Level.WARNING,
                    String.format("Writing of Code 'type: %s / encoding: %s / ident: %s' failed.",
                            codeType.getType(), codeEncoding, identValue.getIdent()), exc);
        }
    }

    /**
     * <p>
     * Configure the barcode error correction depending on the bacode type.
     * </p>
     * @param encodingParameters the encoding parameters where the error correction should be set in.
     */
    private void setErrorCorrection(final Map<EncodeHintType, Object> encodingParameters) {
        switch (codeConfig.getType()) {
            case QRCODE:
                try {
                    encodingParameters.put(EncodeHintType.ERROR_CORRECTION,
                            ErrorCorrectionLevel.valueOf(codeConfig.getErrorCorrection()));
                } catch (IllegalArgumentException | NullPointerException exc) {
                    encodingParameters.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                }
                break;
            case AZTEC:
                if (codeConfig.getErrorCorrection() == null || "".equals(codeConfig.getErrorCorrection())) {
                    encodingParameters.put(EncodeHintType.ERROR_CORRECTION, "25");
                } else {
                    encodingParameters.put(EncodeHintType.ERROR_CORRECTION, codeConfig.getErrorCorrection());
                }
                break;
            default:
                // No error correction for the remaining codes
        }
    }

}