Skip to content

Commit

Permalink
Added more customization options for generating different types of im…
Browse files Browse the repository at this point in the history
…ages with different dimensions. (#1305)
  • Loading branch information
bodiam authored Jul 16, 2024
1 parent c7846a4 commit 83f1187
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 38 deletions.
166 changes: 131 additions & 35 deletions src/main/java/net/datafaker/providers/base/Image.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,97 +3,193 @@

import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;

import static java.awt.Color.WHITE;
import static net.datafaker.providers.base.Image.ImageType.BMP;
import static net.datafaker.providers.base.Image.ImageType.GIF;
import static net.datafaker.providers.base.Image.ImageType.JPEG;
import static net.datafaker.providers.base.Image.ImageType.PNG;
import static net.datafaker.providers.base.Image.ImageType.SVG;
import static net.datafaker.providers.base.Image.ImageType.TIFF;

/**
* Generates base64 encoded PNG, GIF, JPG and SVG images.
* Generates base64 encoded raster and vector images.
*
* @since 2.3.0
*/
public class Image extends AbstractProvider<BaseProviders> {

private static final int WIDTH = 256;
private static final int HEIGHT = 256;
private static final int BOX_SIZE = WIDTH / 8;
private static final int DEFAULT_WIDTH = 256;
private static final int DEFAULT_HEIGHT = 256;

public enum ImageType {
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
BMP("image/bmp"),
GIF("image/gif"),
JPEG("image/jpeg"),
PNG("image/png"),
SVG("image/svg+xml"),
TIFF("image/tiff");

private final String mimeType;

ImageType(String mimeType) {
this.mimeType = mimeType;
}

public String getMimeType() {
return mimeType;
}
}

protected Image(BaseProviders faker) {
super(faker);
}

public String base64PNG() {
return generateBase64Image("png");
public String base64BMP() {
return base64(ImageBuilder.builder().type(BMP).build());
}

public String base64GIF() {
return base64(ImageBuilder.builder().type(GIF).build());
}

public String base64JPG() {
return generateBase64Image("jpeg");
return base64JPEG();
}

public String base64GIF() {
return generateBase64Image("gif");
public String base64JPEG() {
return base64(ImageBuilder.builder().type(JPEG).build());
}

public String base64PNG() {
return base64(ImageBuilder.builder().type(PNG).build());
}

public String base64SVG() {
return generateBase64SVGImage();
return base64(ImageBuilder.builder().type(SVG).build());
}

public String base64TIFF() {
return generateBase64RasterImage(TIFF, DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

public String base64(Base64ImageRuleConfig config) {
if (config.imageType == SVG) {
return generateBase64VectorImage(config.imageType(), config.width(), config.height());
} else {
return generateBase64RasterImage(config.imageType(), config.width(), config.height());
}
}

public record Base64ImageRuleConfig(ImageType imageType, int width, int height) { }

public static class ImageBuilder {
private ImageType imageType = PNG;
private int width = DEFAULT_WIDTH;
private int height = DEFAULT_HEIGHT;

private ImageBuilder() {
}

public static ImageBuilder builder() {
return new ImageBuilder();
}

public Base64ImageRuleConfig build() {
return new Base64ImageRuleConfig(imageType, width, height);
}

public ImageBuilder type(ImageType imageType) {
if(imageType == null) {
throw new IllegalArgumentException("Type cannot be null");
}

this.imageType = imageType;
return this;
}

public ImageBuilder width(int width) {
if (width <= 0) {
throw new IllegalArgumentException("Width must be greater than 0");
}

this.width = width;
return this;
}

public ImageBuilder height(int height) {
if (height <= 0) {
throw new IllegalArgumentException("Height must be greater than 0");
}

this.height = height;
return this;
}
}

private String generateBase64Image(String format) {
BufferedImage bufferedImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
private String generateBase64RasterImage(ImageType imageType, int width, int height) {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = bufferedImage.createGraphics();

int boxSize = Math.max(1, width / 8);

// Fill the image with white background
graphics.setColor(WHITE);
graphics.fillRect(0, 0, WIDTH, HEIGHT);
graphics.fillRect(0, 0, width, height);

// Draw random colored boxes
for (int y = 0; y < HEIGHT; y += BOX_SIZE) {
for (int x = 0; x < WIDTH; x += BOX_SIZE) {
graphics.setColor(randomColor());
graphics.fillRect(x, y, BOX_SIZE, BOX_SIZE);
for (int y = 0; y < height; y += boxSize) {
for (int x = 0; x < width; x += boxSize) {
Color randomColor = randomColor();
graphics.setColor(randomColor);
graphics.fillRect(x, y, boxSize, boxSize);
}
}
graphics.dispose();

try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(bufferedImage, format, baos);
ImageIO.write(bufferedImage, imageType.name(), baos);
byte[] imageBytes = baos.toByteArray();
return "data:image/" + format + ";base64," + Base64.getEncoder().encodeToString(imageBytes);
return "data:" + imageType.mimeType + ";base64," + Base64.getEncoder().encodeToString(imageBytes);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}

private Color randomColor() {
// Convert the bytes to unsigned integers (0-255) for RGB
byte[] randomBytes = faker.random().nextRandomBytes(3);
int red = randomBytes[0] & 0xFF;
int green = randomBytes[1] & 0xFF;
int blue = randomBytes[2] & 0xFF;
return new Color(red, green, blue);
}

private String generateBase64SVGImage() {
private String generateBase64VectorImage(ImageType imageType, int width, int height) {
StringBuilder svg = new StringBuilder();
svg.append("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"").append(WIDTH).append("\" height=\"").append(HEIGHT).append("\">");
svg.append("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"").append(DEFAULT_WIDTH).append("\" height=\"").append(DEFAULT_HEIGHT).append("\">");

int boxSize = Math.max(1, width / 8);

for (int y = 0; y < HEIGHT; y += BOX_SIZE) {
for (int x = 0; x < WIDTH; x += BOX_SIZE) {
for (int y = 0; y < height; y += boxSize) {
for (int x = 0; x < width; x += boxSize) {
Color randomColor = randomColor();
String color = String.format("#%02x%02x%02x", randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
svg.append("<rect x=\"").append(x).append("\" y=\"").append(y).append("\" width=\"").append(BOX_SIZE).append("\" height=\"").append(BOX_SIZE).append("\" fill=\"").append(color).append("\"/>");
svg.append("<rect x=\"").append(x).append("\" y=\"").append(y).append("\" width=\"").append(boxSize).append("\" height=\"").append(boxSize).append("\" fill=\"").append(color).append("\"/>");
}
}

svg.append("</svg>");

String svgString = svg.toString();
String base64Svg = Base64.getEncoder().encodeToString(svgString.getBytes());
return "data:image/svg+xml;base64," + base64Svg;
return "data:" + imageType.mimeType + ";base64," + base64Svg;
}

private Color randomColor() {
// Convert the bytes to unsigned integers (0-255) for RGB
byte[] randomBytes = faker.random().nextRandomBytes(3);
int red = randomBytes[0] & 0xFF;
int green = randomBytes[1] & 0xFF;
int blue = randomBytes[2] & 0xFF;
return new Color(red, green, blue);
}
}
96 changes: 94 additions & 2 deletions src/test/java/net/datafaker/providers/base/ImageTest.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
package net.datafaker.providers.base;

import net.datafaker.providers.base.Image.ImageType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;

class ImageTest extends BaseFakerTest<BaseFaker> {

@Test
void bmp() {
assertThat(faker.image().base64BMP()).startsWith("data:image/bmp;base64,");
}

@Test
void gif() {
assertThat(faker.image().base64GIF()).startsWith("data:image/gif;base64,");
}

@Test
void png() {
assertThat(faker.image().base64PNG()).startsWith("data:image/png;base64,");
Expand All @@ -17,12 +31,90 @@ void jpg() {
}

@Test
void gif() {
assertThat(faker.image().base64GIF()).startsWith("data:image/gif;base64,");
void jpeg() {
assertThat(faker.image().base64JPEG()).startsWith("data:image/jpeg;base64,");
}

@Test
void svg() {
assertThat(faker.image().base64SVG()).startsWith("data:image/svg+xml;base64,");
}

@Test
void tiff() {
assertThat(faker.image().base64TIFF()).startsWith("data:image/tiff;base64,");
}

@ParameterizedTest
@EnumSource(ImageType.class)
void base64(ImageType imageType) {
String base64Image = faker.image().base64(new Image.Base64ImageRuleConfig(imageType, 1000, 1000));

assertThat(base64Image)
.startsWith("data:" + imageType.getMimeType() + ";base64,");
assertThat(base64Image.substring(base64Image.indexOf(",") + 1))
.isNotBlank()
.isBase64();
}

@Test
void defaultBuilder() {
String image = faker.image().base64(Image.ImageBuilder.builder()
.build());
assertThat(image).startsWith("data:image/");
}

@Test
void customBase64builder() {
String gif = faker.image().base64(Image.ImageBuilder.builder()
.type(ImageType.GIF)
.build());
assertThat(gif).startsWith("data:image/gif;base64,");
}

@Test
void tinyBase64builder() {
String tiny = faker.image().base64(Image.ImageBuilder.builder()
.height(1)
.width(1)
.type(ImageType.PNG)
.build());

assertThat(tiny).startsWith("data:image/png;base64,");
}

@Test
void largeBase64builder() {
String large = faker.image().base64(Image.ImageBuilder.builder()
.height(1000)
.width(2000)
.type(ImageType.BMP)
.build());
assertThat(large).startsWith("data:image/bmp;base64,");
}

@Test
void shouldErrorOnIllegalType() {
assertThatIllegalArgumentException().isThrownBy(() -> Image.ImageBuilder.builder().type(null).build());
}

@Test
void shouldErrorOnNegativeWidth() {
assertThatIllegalArgumentException().isThrownBy(() -> Image.ImageBuilder.builder().width(-1).build());
}

@Test
void shouldErrorOnZeroWidth() {
assertThatIllegalArgumentException().isThrownBy(() -> Image.ImageBuilder.builder().width(0).build());
}

@Test
void shouldErrorOnNegativeHeight() {
assertThatIllegalArgumentException().isThrownBy(() -> Image.ImageBuilder.builder().height(-1).build());
}

@Test
void shouldErrorOnZeroHeight() {
assertThatIllegalArgumentException().isThrownBy(() -> Image.ImageBuilder.builder().height(0).build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public static void main(String[] args) {
System.out.println("PNG Image:");
System.out.println(faker.image().base64PNG());
System.out.println("JPG Image:");
System.out.println(faker.image().base64JPG());
System.out.println(faker.image().base64JPEG());
System.out.println("GIF Image:");
System.out.println(faker.image().base64GIF());
System.out.println("SVG Image:");
Expand Down

0 comments on commit 83f1187

Please sign in to comment.