Generate QR codes with custom dot shapes using zxing

别说谁变了你拦得住时间么 提交于 2020-04-30 07:49:12

问题


I'm writing an application to generate QR codes with custom dot shapes. What's the best way to do this using zxing?

So far, I've dug through the source code and I see that the data bits are written in com.google.zxing.qrcode.encoder.MatrixUtil.embedDataBits(). I think I could add some code on to the end of this function which would allow me to mask the dots but I'm not sure how to do this in Java. I can't extend the class because it's declared as final. Would it be a good idea and if so how would I extend this method in that way?

The other option I've been looking at involves post-processing the image produced by QRCode but this is really complex I think as I'd have to find a way to discern the dots from the positioning squares.

Is there a better way to do what I'm looking to do? Is there another QR code library besides zxing which can do what I'm looking to do out of the box?

P.S. I want to note that this is not a duplicate of this question although the keywords are similar.


回答1:


I ended up forking zxing and using JitPack to include it with Maven. I implemented functionality for RGB masking and for drawing circles and outlined squares as dot shapes. Here is the repository: https://github.com/flotwig/zxing




回答2:


The following java code uses zxing to make a QR-code image with circular dots and a circular finder pattern (custom rendering style). This can be adapted to other custom render styles.

I use the Encoder class directly and bypass QRCodeWriter and MatrixToImageWriter to gain enough control to alter the rendering. To alter the finder pattern, I use the fact that the finder pattern is always 7 dots wide/tall. Otherwise I would have to create a custom version of MatrixUtil (and perhaps Encoder).

Example QR Code Image Generated:

    public static void main(String[] args) {
        try {
            generateQRCodeImage("https://www.google.com", 300, 300, "./MyQRCode.png");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException {
        final Map<EncodeHintType, Object> encodingHints = new HashMap<>();
        encodingHints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        QRCode code = Encoder.encode(text, ErrorCorrectionLevel.H, encodingHints);
        BufferedImage image = renderQRImage(code, width, height, 4);

        try (FileOutputStream stream = new FileOutputStream(filePath)) {
            stream.write(bufferedImageToBytes(image));
        }
    }

    private static BufferedImage renderQRImage(QRCode code, int width, int height, int quietZone) {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D graphics = image.createGraphics();

        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setBackground(Color.white);
        graphics.clearRect(0, 0, width, height);
        graphics.setColor(Color.black);

        ByteMatrix input = code.getMatrix();
        if (input == null) {
            throw new IllegalStateException();
        }
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int qrWidth = inputWidth + (quietZone * 2);
        int qrHeight = inputHeight + (quietZone * 2);
        int outputWidth = Math.max(width, qrWidth);
        int outputHeight = Math.max(height, qrHeight);

        int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
        int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
        int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
        final int FINDER_PATTERN_SIZE = 7;
        final float CIRCLE_SCALE_DOWN_FACTOR = 21f/30f;
        int circleSize = (int) (multiple * CIRCLE_SCALE_DOWN_FACTOR);

        for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
            for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
                if (input.get(inputX, inputY) == 1) {
                    if (!(inputX <= FINDER_PATTERN_SIZE && inputY <= FINDER_PATTERN_SIZE ||
                          inputX >= inputWidth - FINDER_PATTERN_SIZE && inputY <= FINDER_PATTERN_SIZE ||
                          inputX <= FINDER_PATTERN_SIZE && inputY >= inputHeight - FINDER_PATTERN_SIZE)) {
                        graphics.fillOval(outputX, outputY, circleSize, circleSize);
                    }
                }
            }
        }

        int circleDiameter = multiple * FINDER_PATTERN_SIZE;
        drawFinderPatternCircleStyle(graphics, leftPadding, topPadding, circleDiameter);
        drawFinderPatternCircleStyle(graphics, leftPadding + (inputWidth - FINDER_PATTERN_SIZE) * multiple, topPadding, circleDiameter);
        drawFinderPatternCircleStyle(graphics, leftPadding, topPadding + (inputHeight - FINDER_PATTERN_SIZE) * multiple, circleDiameter);

        return image;
    }

Maven dependency:

    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>core</artifactId>
        <version>3.4.0</version>
    </dependency>


来源:https://stackoverflow.com/questions/35419511/generate-qr-codes-with-custom-dot-shapes-using-zxing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!