Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the ability to add logo to svg qrcodes #161

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions javase/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-dom</artifactId>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
51 changes: 36 additions & 15 deletions javase/src/main/java/net/glxn/qrgen/javase/MatrixToSvgWriter.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
package net.glxn.qrgen.javase;

import java.awt.Color;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.file.Path;

import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.common.BitMatrix;
import org.apache.batik.dom.GenericDOMImplementation;
import org.apache.batik.svggen.SVGGraphics2D;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;

import java.awt.*;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.file.Path;

class MatrixToSvgWriter {

private MatrixToSvgWriter() {
// private utility class constuctor
}

private static SVGGraphics2D toSvgDocument(BitMatrix matrix, MatrixToImageConfig config) {
private static SVGGraphics2D toSvgDocument(BitMatrix matrix, QRCode qrCode) {

int width = matrix.getWidth();
int height = matrix.getHeight();
Expand All @@ -29,32 +31,51 @@ private static SVGGraphics2D toSvgDocument(BitMatrix matrix, MatrixToImageConfig
String svgNS = "http://www.w3.org/2000/svg";
Document document = domImpl.createDocument(svgNS, "svg", null);

MatrixToImageConfig config = qrCode.matrixToImageConfig;

SVGGraphics2D svgGraphics = new SVGGraphics2D(document);
svgGraphics.setColor(new Color(config.getPixelOffColor()));
svgGraphics.fillRect(0,0, width, height);
svgGraphics.fillRect(0, 0, width, height);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (matrix.get(x,y)) {
if (matrix.get(x, y)) {
svgGraphics.setColor(new Color(config.getPixelOnColor()));
svgGraphics.fillRect(x, y, 1, 1);
}
}
}

return svgGraphics;
if (qrCode.logo != null) {
int logoXLocation = (width - qrCode.logoWidth) / 2;
int logoYLocation = (height - qrCode.logoHeight) / 2;
svgGraphics.drawImage(
qrCode.logo,
logoXLocation,
logoYLocation,
qrCode.logoWidth,
qrCode.logoHeight,
new ImageObserver() {

@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
return true;
}
});
}

return svgGraphics;

}

static void writeToStream(BitMatrix matrix, OutputStream outs, MatrixToImageConfig matrixToImageConfig) throws IOException {
SVGGraphics2D g2 = toSvgDocument(matrix, matrixToImageConfig);
static void writeToStream(BitMatrix matrix, OutputStream outs, QRCode qrCode) throws IOException {
SVGGraphics2D g2 = toSvgDocument(matrix, qrCode);

OutputStreamWriter out = new OutputStreamWriter(outs);
g2.stream(out);
}

static void writeToPath(BitMatrix matrix, Path file, MatrixToImageConfig matrixToImageConfig) throws IOException {
SVGGraphics2D g2 = toSvgDocument(matrix, matrixToImageConfig);
static void writeToPath(BitMatrix matrix, Path file, QRCode qrCode) throws IOException {
SVGGraphics2D g2 = toSvgDocument(matrix, qrCode);

FileWriter out = new FileWriter(file.toFile());
g2.stream(out);
Expand Down
61 changes: 41 additions & 20 deletions javase/src/main/java/net/glxn/qrgen/javase/QRCode.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package net.glxn.qrgen.javase;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageConfig;
Expand All @@ -11,17 +16,19 @@
import net.glxn.qrgen.core.image.ImageType;
import net.glxn.qrgen.core.scheme.Schema;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

public class QRCode extends AbstractQRCode {

public static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();

protected final String text;
protected MatrixToImageConfig matrixToImageConfig = DEFAULT_CONFIG;

protected BufferedImage logo;

protected int logoWidth;

protected int logoHeight;

protected QRCode(String text) {
this.text = text;
qrWriter = new QRCodeWriter();
Expand All @@ -30,16 +37,14 @@ protected QRCode(String text) {
/**
* Create a QR code from the given text. <br><br>
* <p>
* There is a size limitation to how much you can put into a QR code. This has been tested to work with up to a length of
* 2950
* characters.<br><br>
* There is a size limitation to how much you can put into a QR code. This has been tested to work with up to a
* length of 2950 characters.<br><br>
* </p>
* <p>
* The QRCode will have the following defaults: <br> {size: 100x100}<br>{imageType:PNG} <br><br>
* </p>
* Both size and imageType can be overridden: <br> Image type override is done by calling {@link
* QRCode#to(ImageType)} e.g. QRCode.from("hello world").to(JPG) <br> Size override is done
* by calling
* Both size and imageType can be overridden: <br> Image type override is done by calling
* {@link QRCode#to(ImageType)} e.g. QRCode.from("hello world").to(JPG) <br> Size override is done by calling
* {@link QRCode#withSize} e.g. QRCode.from("hello world").to(JPG).withSize(125, 125) <br>
*
* @param text the text to encode to a new QRCode, this may fail if the text is too large. <br>
Expand All @@ -54,6 +59,7 @@ public static QRCode from(String text) {
* <p>
* The QRCode will have the following defaults: <br> {size: 100x100}<br>{imageType:PNG} <br><br>
* </p>
*
* @param schema the schema to encode as QRCode
* @return the QRCode object
*/
Expand All @@ -75,7 +81,7 @@ public QRCode to(ImageType imageType) {
/**
* Overrides the size of the qr from its default 125x125
*
* @param width the width in pixels
* @param width the width in pixels
* @param height the height in pixels
* @return the current QRCode object
*/
Expand All @@ -86,8 +92,8 @@ public QRCode withSize(int width, int height) {
}

/**
* Overrides the default charset by supplying a {@link com.google.zxing.EncodeHintType#CHARACTER_SET} hint to {@link
* com.google.zxing.qrcode.QRCodeWriter#encode}
* Overrides the default charset by supplying a {@link com.google.zxing.EncodeHintType#CHARACTER_SET} hint to
* {@link com.google.zxing.qrcode.QRCodeWriter#encode}
*
* @param charset the charset as string, e.g. UTF-8
* @return the current QRCode object
Expand All @@ -97,8 +103,8 @@ public QRCode withCharset(String charset) {
}

/**
* Overrides the default error correction by supplying a {@link com.google.zxing.EncodeHintType#ERROR_CORRECTION} hint to
* {@link com.google.zxing.qrcode.QRCodeWriter#encode}
* Overrides the default error correction by supplying a {@link com.google.zxing.EncodeHintType#ERROR_CORRECTION}
* hint to {@link com.google.zxing.qrcode.QRCodeWriter#encode}
*
* @param level the error correction level to use by {@link com.google.zxing.qrcode.QRCodeWriter#encode}
* @return the current QRCode object
Expand All @@ -124,7 +130,11 @@ public File file() {
File file;
try {
file = createTempFile();
MatrixToImageWriter.writeToPath(createMatrix(text), imageType.toString(), file.toPath(), matrixToImageConfig);
MatrixToImageWriter.writeToPath(
createMatrix(text),
imageType.toString(),
file.toPath(),
matrixToImageConfig);
} catch (Exception e) {
throw new QRGenerationException("Failed to create QR image from text due to underlying exception", e);
}
Expand All @@ -137,7 +147,11 @@ public File file(String name) {
File file;
try {
file = createTempFile(name);
MatrixToImageWriter.writeToPath(createMatrix(text), imageType.toString(), file.toPath(), matrixToImageConfig);
MatrixToImageWriter.writeToPath(
createMatrix(text),
imageType.toString(),
file.toPath(),
matrixToImageConfig);
} catch (Exception e) {
throw new QRGenerationException("Failed to create QR image from text due to underlying exception", e);
}
Expand All @@ -154,7 +168,7 @@ public File svg() {
File file;
try {
file = createTempSvgFile();
MatrixToSvgWriter.writeToPath(createMatrix(text), file.toPath(), matrixToImageConfig);
MatrixToSvgWriter.writeToPath(createMatrix(text), file.toPath(), this);
} catch (Exception e) {
throw new QRGenerationException("Failed to create QR svg from text due to underlying exception", e);
}
Expand All @@ -165,7 +179,7 @@ public File svg(String name) {
File file;
try {
file = createTempSvgFile(name);
MatrixToSvgWriter.writeToPath(createMatrix(text), file.toPath(), matrixToImageConfig);
MatrixToSvgWriter.writeToPath(createMatrix(text), file.toPath(), this);
} catch (Exception e) {
throw new QRGenerationException("Failed to create QR svg from text due to underlying exception", e);
}
Expand All @@ -174,7 +188,7 @@ public File svg(String name) {

public void svg(OutputStream outs) {
try {
MatrixToSvgWriter.writeToStream(createMatrix(text), outs, matrixToImageConfig);
MatrixToSvgWriter.writeToStream(createMatrix(text), outs, this);
} catch (Exception e) {
throw new QRGenerationException("Failed to create QR svg from text due to underlying exception", e);
}
Expand All @@ -194,4 +208,11 @@ public QRCode withColor(int onColor, int offColor) {
matrixToImageConfig = new MatrixToImageConfig(onColor, offColor);
return this;
}

public QRCode withLogo(BufferedImage bufferedImage, int logoWidth, int logoHeight) {
this.logo = bufferedImage;
this.logoWidth = logoWidth;
this.logoHeight = logoHeight;
return this;
}
}
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@
<artifactId>batik-dom</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.16</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
Expand Down