How to add logo inside QR code

0
Hello, I am using https://marketplace.mendix.com/link/component/118612 marketplace widget for QR code image generation. I am able to create QR code image. But I would like to also add the logo in the center of the generated QR-image. can anyone help me is this possible?   Example:
asked
1 answers
2

I did this a few months ago using a java action and ImageIO.

 

Java action parameters:

ImageBackground: System.Image, i.e. your QR image

ImageOnTop: System.Image, smaller logo image

ImageResult: System.Image in which resulting QR with logo is stored

ImageTopMaxSize: Integer for max width/height of your logo

BorderWidth: Integer width in px of surrounding white border

BorderSquare: Boolean, when false, ImageOnTop is supposed to be a circle or oval with transparency around

BorderBlack: Boolean to set border black instead of white

WhiteBgToTransparent: Boolean, whether or not to convert white bg to transparency when BorderSquare = false

 

 

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import javax.imageio.ImageIO;

 

 

// BEGIN USER CODE
// read input file contents
InputStream ImageBgStream = Core.getImage(this.context(), this.ImageBackground.getMendixObject(), false);
InputStream ImageTopStream = Core.getImage(this.context(), this.ImageOnTop.getMendixObject(), false);

BufferedImage source = ImageIO.read(ImageBgStream);
BufferedImage logo = ImageIO.read(ImageTopStream);

// turn white background (edges only) transparent
if (!this.BorderSquare && this.WhiteBgToTransparent) {
	// log.debug("executing makeBackgroundTransparent");
	BufferedImage target = new BufferedImage(logo.getWidth(), logo.getHeight(), BufferedImage.TYPE_INT_ARGB);
	makeBackgroundTransparent(logo, target);
	logo = target;
}

int borderWidth = this.BorderWidth.intValue();
int logoSize = this.ImageTopMaxSize.intValue();

// calculate logo size with border substracted and scale logo
int innerLogoSize = logoSize - borderWidth - borderWidth;

// check which of height/width is greater and scale that to size, keeping aspect ratio by setting the other negative
boolean WidthOverHeight = (logo.getWidth() >= logo.getHeight());

int innerLogoWidth = (WidthOverHeight) ? innerLogoSize : -1;
int innerLogoHeight = (WidthOverHeight) ? -1 : innerLogoSize;

Image scaledLogo = logo.getScaledInstance(innerLogoWidth, innerLogoHeight, Image.SCALE_SMOOTH);

int sourceHeight = source.getHeight();
int sourceWidth = source.getWidth();

int scaledLogoWidth = WidthOverHeight ? innerLogoSize : (logo.getWidth() * innerLogoSize / logo.getHeight());
int scaledLogoHeight = WidthOverHeight ? (logo.getHeight() * innerLogoSize / logo.getWidth()) : innerLogoSize;

int logoHeight = scaledLogoHeight + (borderWidth * 2);
int logoWidth = scaledLogoWidth + (borderWidth * 2);

// calculate x and y coordinates for border
int xParam = (int) Math.ceil((sourceWidth - logoWidth) / 2);
int yParam = (int) Math.ceil((sourceHeight - logoHeight) / 2);

// now calculate x and y coordinates for inner logo
int xParamInner = xParam + borderWidth;
int yParamInner = yParam + borderWidth;

// create new image with ARGB (in case source QR is binary black and white) 
// and draw source + (white) border + logo
BufferedImage result = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = result.createGraphics();
g.drawImage(source, 0, 0, null);
Color borderColor = this.BorderBlack ? Color.BLACK : Color.WHITE;
g.setColor(borderColor);
if (this.BorderSquare) {
	g.fillRect(xParam, yParam, logoWidth, logoHeight);
} else {
	g.fillOval(xParam, yParam, logoWidth, logoHeight);
}
g.drawImage(scaledLogo, xParamInner, yParamInner, null);

// write source to new outStream
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageIO.write(result, "png", outStream);

// write outStream to result file with thumbnail 100x100
InputStream is = new ByteArrayInputStream(outStream.toByteArray());
Core.storeImageDocumentContent(this.context(), this.ImageResult.getMendixObject(), is, 200, 200);

// tidy up
g.dispose();
ImageBgStream.close();
ImageTopStream.close();
outStream.close();
is.close();

return null;
// END USER CODE

 

	// BEGIN EXTRA CODE
        private static final ILogNode log = Core.getLogger("TestQR");
        
        private static final int tolerance = 1000000; // value chosen through several attempts
        private static final int ColorWhite = Color.WHITE.getRGB();
        
        // https://stackoverflow.com/questions/3101446/how-to-convert-an-image-into-a-transparent-image-in-java
        /**
        * Given an image with no transparency, it makes the white background
        * transparent, provided that the entire image outline has a different color
        * from the background; the internal pixels of the image, even if they have
        * the same color as the background, are not changed.
        *
        * @param source image with a white background; the image must have an
        * outline of a different color from background.
        * @param target new image with a transparent background
        */
        private static java.lang.Void makeBackgroundTransparent(BufferedImage source, BufferedImage target) {
            /*
             * Algorithm
             *
             * Pixels must be iterated in the four possible directions: (1) left to
             * right, for each row (top to bottom); (2) from right to left, for each
             * row (from top to bottom); (3) from top to bottom, for each column
             * (from left to right); (4) from bottom to top, for each column (from
             * left to right).
             *
             * In each iteration, each white pixel is replaced with a transparent
             * one. Each iteration ends when a pixel of color other than white (or
             * a transparent pixel) is encountered.
             */
            if (source == null) {
                throw new IllegalArgumentException("ImageUtilities.makeBackgroundTransparent -> null source image");
            }
            
            
            int width = source.getWidth();
            int height = source.getHeight();
            int[] pixels = source.getRGB(0, 0, width, height, null, 0, width); // array instance containing the ARGB data within this image

            // check if the first pixel is transparent
            if ((pixels[0] >> 24) == 0x00) {
                return null; // nothing to do, the image already has a transparent background
            }
            

            // 1. Left to right, for each row (top to bottom)
            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    if (!pixelConverted(pixels, y * width + x)) {
                        break;
                    }
                }
            }

            // 2. Right to left, for each row (top to bottom)
            for (int y = 0; y < height; y++) {
                for (int x = width - 1; x >= 0; x--) {
                    if (!pixelConverted(pixels, y * width + x)) {
                        break;
                    }
                }
            }

            // 3. Top to bottom, for each column (from left to right)
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    if (!pixelConverted(pixels, y * width + x)) {
                        break;
                    }
                }
            }

            // 4. Bottom to top, for each column (from left to right)
            for (int x = 0; x < width; x++) {
                for (int y = height - 1; y >= 0; y--) {
                    if (!pixelConverted(pixels, y * width + x)) {
                        break;
                    }
                }
            }
            target.setRGB(0, 0, width, height, pixels, 0, width);
            return null;
        }
        
        private static boolean pixelConverted(int[] pixels, int index) {
            int color = pixels[index];
            if ((color >> 24) != 0x00 && color >= ColorWhite - tolerance && color <= ColorWhite + tolerance) { // means white with tolerance and no transparency
                pixels[index] = 0x00; // means full transparency
                return true;
            } else {
                return false;
            }
        }
        
	// END EXTRA CODE

 

answered