Believe it or not, I am making my own company? it also means that I was thinking about a business card and being an IT guy in addition to my sense of coaching I thought about digital one!

I have therefore searched for a custom QRCode, and I will provide here what I have done and maybe someone else will also be inspired.

After a quick google search, I found zxing. At heart is it a pure Java library for decoding barcodes (the core/ and javase/ modules). It also contains several applications for Android, Google Glass, a JavaEE web application, and a GWT-based encoder application.

My initial idea was to combine the vcard with & QRcode, the main restriction after some research is the limitation brought by the QRCode in term of amount of data.

Of course, you can pay for it, but I will provide a small piece of code to generate one for you.
Below is the QRCode generator, of course, to make it more robust, additional work would be required but the spirit is there, and that is my objective.

package lu.acsolutions.QRCode;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

public class QRCodeGenerator {

	private String DIR;
	private String ext;
	private String LOGO;
	private String CONTENT;
	private int WIDTH;
	private int HEIGHT;

	public void generate() {
		// Create new configuration that specifies the error correction, below is High
		// which provide the best
		// QRCode reader will be working more easily.
		Map<EncodeHintType, ErrorCorrectionLevel> hints = new HashMap<>();
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);

		QRCodeWriter writer = new QRCodeWriter();

		BitMatrix bitMatrix = null;

		ByteArrayOutputStream os = new ByteArrayOutputStream();

		try {
			// init directory
			cleanDirectory(DIR);
			initDirectory(DIR);

			/*
			 * Code below I have used to check accordingly to the amount of data, which
			 * version was used not really important, it was a quick debug for me.
			 *
			 * QRCode qrcode = com.google.zxing.qrcode.encoder.Encoder.encode(CONTENT,
			 * ErrorCorrectionLevel.L); int qrCodeVersion =
			 * qrcode.getVersion().getVersionNumber();
			 * System.out.println("hello this is the version:" +
			 * Integer.toString(qrCodeVersion));
			 */
			// Create a qr code with the url as content and a size of WxH px
			bitMatrix = writer.encode(CONTENT, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, hints);

			// Load QR image
			BufferedImage qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix, getMatrixConfig());

			// Load logo image
			BufferedImage overly = getOverly(LOGO);

			// Resize to be smaller than the QRCode, indeed my first test was a with a Logo
			// which was even bigger than my QRCode
			// it is embedded to my code but could be easily become a property for the
			// class.
			overly = resize(overly, qrImage.getHeight() / 4, qrImage.getWidth() / 4);

			// Calculate the delta height and width between QR code and logo
			int deltaHeight = qrImage.getHeight() - overly.getHeight();
			int deltaWidth = qrImage.getWidth() - overly.getWidth();

			// Initialise combined image
			BufferedImage combined = new BufferedImage(qrImage.getHeight(), qrImage.getWidth(),
					BufferedImage.TYPE_INT_ARGB);
			Graphics2D g = (Graphics2D) combined.getGraphics();

			// Write QR code to new image at position 0/0
			g.drawImage(qrImage, 0, 0, null);
			g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1f));

			// Write logo into combine image at position (deltaWidth / 2) and
			// (deltaHeight / 2). Background: Left/Right and Top/Bottom must be
			// the same space for the logo to be centered
			g.drawImage(overly, (int) Math.round(deltaWidth / 2), (int) Math.round(deltaHeight / 2), null);

			// Write combined image as PNG to OutputStream
			ImageIO.write(combined, "png", os);
			// Store Image
			Files.copy(new ByteArrayInputStream(os.toByteArray()),
					Paths.get(DIR + generateRandoTitle(new Random(), 9) + ext), StandardCopyOption.REPLACE_EXISTING);

		} catch (WriterException e) {
			e.printStackTrace();
			// LOG.error("WriterException occured", e);
		} catch (IOException e) {
			e.printStackTrace();
			// LOG.error("IOException occured", e);
		}
	}

	public String getDIR() {
		return DIR;
	}

	public void setDIR(String dIR) {
		DIR = dIR;
	}

	public String getExt() {
		return ext;
	}

	public void setExt(String ext) {
		this.ext = ext;
	}

	public String getLOGO() {
		return LOGO;
	}

	public void setLOGO(String lOGO) {
		LOGO = lOGO;
	}

	public String getCONTENT() {
		return CONTENT;
	}

	public void setCONTENT(String cONTENT) {
		CONTENT = cONTENT;
	}

	public int getWIDTH() {
		return WIDTH;
	}

	public void setWIDTH(int wIDTH) {
		WIDTH = wIDTH;
	}

	public int getHEIGHT() {
		return HEIGHT;
	}

	public void setHEIGHT(int hEIGHT) {
		HEIGHT = hEIGHT;
	}

	private BufferedImage getOverly(String LOGO) throws IOException {
		URL url = new URL(LOGO);
		return ImageIO.read(url);
	}

	public BufferedImage resize(BufferedImage img, int height, int width) {
		Image tmp = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
		BufferedImage resized = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2d = resized.createGraphics();
		g2d.drawImage(tmp, 0, 0, null);
		g2d.dispose();
		return resized;
	}

	private void initDirectory(String DIR) throws IOException {
		Files.createDirectories(Paths.get(DIR));
	}

	private void cleanDirectory(String DIR) {
		try {
			Files.walk(Paths.get(DIR), FileVisitOption.FOLLOW_LINKS).sorted(Comparator.reverseOrder()).map(Path::toFile)
					.forEach(File::delete);
		} catch (IOException e) {
			// Directory does not exist, Do nothing
		}
	}

	private MatrixToImageConfig getMatrixConfig() {
		// ARGB Colors
		// Check Colors ENUM
		return new MatrixToImageConfig(Colors.PURPLE.getArgb(), Colors.BLACK.getArgb());
	}

	private String generateRandoTitle(Random random, int length) {
		return random.ints(48, 122).filter(i -> (i < 57 || i > 65) &amp;&amp; (i < 90 || i > 97)).mapToObj(i -> (char) i)
				.limit(length).collect(StringBuilder::new, StringBuilder::append, StringBuilder::append).toString();
	}

}

Now the main class, in this one I have actually use the vcard from ezvcard to setup the ad-hoc properties.

package lu.acsolutions.QRCode;

import ezvcard.Ezvcard;
import ezvcard.VCard;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.ImageType;
import ezvcard.parameter.TelephoneType;
import ezvcard.property.Logo;
import ezvcard.property.Organization;
import ezvcard.property.Photo;
import ezvcard.property.StructuredName;
import ezvcard.property.Telephone;
import ezvcard.property.Uid;
import ezvcard.util.TelUri;

public class GenerateQRCode {

	private static final String QR_CODE_IMAGE_PATH = "./MyQRCode/";
	//Of course this below will need to be changed accordingly, otherwise you are going to have my own logo ;-)
	private static final String QR_CODE_LOGO = "https://acsolutions.lu/wp-content/uploads/2019/08/AC-Soutions2.jpg";
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		QRCodeGenerator qrCode = new QRCodeGenerator();
		

		VCard vcard = new VCard();
		StructuredName n = new StructuredName();
		n.setFamily("Claus");
		n.setGiven("Alexandre");
		n.getPrefixes().add("Mr");
		vcard.setStructuredName(n);
		vcard.setFormattedName("Alexandre Claus");

		TelUri uri = new TelUri.Builder("+xxx-xxxxxxx").build();
		Telephone tel = new Telephone(uri);
		tel.getTypes().add(TelephoneType.CELL);
		vcard.addTelephoneNumber(tel);
		
		vcard.addUrl("http://www.acsolutions.lu");
		
		//generate a random UID
		Uid uid = Uid.random();
		vcard.setUid(uid);
      
    	Photo photo = new Photo("https://acsolutions.lu/wp-content/uploads/2019/07/AC.png", ImageType.JPEG);
        vcard.addPhoto(photo);
		
		vcard.addEmail("alexandre.claus@gmail.com", EmailType.HOME);
		vcard.addEmail("alexandre.claus@acsolutions.lu", EmailType.WORK);

		Logo logo = new Logo("https://acsolutions.lu/wp-content/uploads/2019/08/AC-Soutions2.jpg", ImageType.JPEG);
		vcard.addLogo(logo);
		 
		Organization org = new Organization();
		org.getValues().add("Agile &amp; Coaching Solutions");
		org.getValues().add("Co-founder");
		vcard.setOrganization(org);
		
		System.out.println(Ezvcard.write(vcard).go());
		
		qrCode.setDIR(QR_CODE_IMAGE_PATH);
		qrCode.setCONTENT(Ezvcard.write(vcard).go());
		qrCode.setExt(".jpg");
		qrCode.setHEIGHT(300);
		qrCode.setWIDTH(300);
		qrCode.setLOGO(QR_CODE_LOGO);
		//Now we generate!
		qrCode.generate();
	
	}
}

I have almost forgotten, you will also need this little enum class, in order to set the colors, this can easily be adapted for your own purpose.

package lu.acsolutions.QRCode;

 enum Colors {

    BLUE(0xFF40BAD0),
    RED(0xFFE91C43),
    PURPLE(0xFF8A4F9E),
    ORANGE(0xFFF4B13D),
    WHITE(0xFFFFFFFF),
    BLACK(0xFF000000);

    private final int argb;

    Colors(final int argb){
        this.argb = argb;
    }

    public int getArgb(){
        return argb;
    }
}

This is after running my code, lovely isn’t it??