From cfe3f4b89139df00a3fb79973c7c919d70a2cfd0 Mon Sep 17 00:00:00 2001 From: melloware Date: Sat, 2 Nov 2024 10:32:28 -0400 Subject: [PATCH] Add Okapi utility class similar to ZebraCrossing --- .../barcode/it/BaseImageResource.java | 15 + .../quarkiverse/barcode/it/OkapiResource.java | 58 ++- .../barcode/it/OkapiResourceTest.java | 25 ++ .../io/quarkiverse/barcode/okapi/Okapi.java | 361 ++++++++++++++++++ .../barcode/zxing/ZebraCrossing.java | 49 ++- 5 files changed, 476 insertions(+), 32 deletions(-) create mode 100644 ext-okapi/runtime/src/main/java/io/quarkiverse/barcode/okapi/Okapi.java diff --git a/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/BaseImageResource.java b/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/BaseImageResource.java index d94da92..452c1e7 100644 --- a/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/BaseImageResource.java +++ b/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/BaseImageResource.java @@ -23,6 +23,21 @@ protected Response buildImageResponse(byte[] imageData, String mimeType, String return response.build(); } + /** + * Builds a Response containing image data with appropriate headers. + * + * @param imageData The raw bytes of the image + * @param mimeType The MIME type of the image (e.g. "image/png") + * @param fileName The filename to use in the Content-Disposition header + * @return A Response object configured with the image data and headers + */ + protected Response buildImageResponse(String imageData, String mimeType, String fileName) { + final Response.ResponseBuilder response = Response.ok(imageData); + response.header(HttpHeaders.CONTENT_DISPOSITION, CONTENT_DISPOSITION.formatted(fileName)); + response.header(HttpHeaders.CONTENT_TYPE, mimeType); + return response.build(); + } + protected String stripWhitespace(String svgContent) { // Remove unnecessary whitespace characters (spaces, tabs, newlines) from SVG content return svgContent diff --git a/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/OkapiResource.java b/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/OkapiResource.java index 73c7210..9f5c4a1 100644 --- a/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/OkapiResource.java +++ b/ext-okapi/integration-tests/src/main/java/io/quarkiverse/barcode/it/OkapiResource.java @@ -16,14 +16,10 @@ */ package io.quarkiverse.barcode.it; -import java.awt.Graphics2D; import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import javax.imageio.ImageIO; - import jakarta.enterprise.context.ApplicationScoped; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -35,11 +31,9 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import io.quarkiverse.barcode.okapi.Okapi; import uk.org.okapibarcode.backend.Code128; import uk.org.okapibarcode.backend.HumanReadableLocation; -import uk.org.okapibarcode.graphics.Color; -import uk.org.okapibarcode.output.Java2DRenderer; -import uk.org.okapibarcode.output.SvgRenderer; @Path("/okapi") @ApplicationScoped @@ -49,36 +43,40 @@ public class OkapiResource extends BaseImageResource { @Path("code128/png") @APIResponse(responseCode = "200", description = "Document downloaded", content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM, schema = @Schema(type = SchemaType.STRING, format = "binary"))) public Response code128() throws IOException { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - Code128 barcode = createCode128(); - - int width = barcode.getWidth(); - int height = barcode.getHeight(); - - BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); - Graphics2D g2d = image.createGraphics(); - Java2DRenderer renderer = new Java2DRenderer(g2d, 1, Color.WHITE, Color.BLACK); - renderer.render(barcode); - g2d.dispose(); + Code128 barcode = createCode128(); - ImageIO.write(image, "png", outputStream); + int width = barcode.getWidth(); + int height = barcode.getHeight(); - // return the image - return buildImageResponse(outputStream.toByteArray(), PNG_MIME_TYPE, "okapi-code128.png"); - } + BufferedImage image = Okapi.generateBarcodePng(barcode, width, height); + byte[] bytes = Okapi.pngToBytes(image); + // return the image + return buildImageResponse(bytes, PNG_MIME_TYPE, "okapi-code128.png"); } @GET @Path("code128/svg") @APIResponse(responseCode = "200", description = "Document downloaded", content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM, schema = @Schema(type = SchemaType.STRING, format = "binary"))) public Response code128Svg() throws IOException { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - Code128 barcode = createCode128(); - SvgRenderer renderer = new SvgRenderer(outputStream, 1, Color.WHITE, Color.BLACK, true); - renderer.render(barcode); - String svg = stripWhitespace(outputStream.toString()); - return buildImageResponse(svg.getBytes(StandardCharsets.UTF_8), SVG_MIME_TYPE, "okapi-code128.svg"); - } + Code128 barcode = createCode128(); + String svg = stripWhitespace(Okapi.generateBarcodeSvgAsString(barcode)); + return buildImageResponse(svg.getBytes(StandardCharsets.UTF_8), SVG_MIME_TYPE, "okapi-code128.svg"); + } + + @GET + @Path("ean13/png/base64") + @APIResponse(responseCode = "200", description = "Document downloaded", content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM, schema = @Schema(type = SchemaType.STRING, format = "binary"))) + public Response ean13PngBase64() throws IOException { + String image = Okapi.ean13Png("123456789012+12345", 300, 300); + return buildImageResponse(image, PNG_MIME_TYPE, "okapi-ean13.png"); + } + + @GET + @Path("ean13/svg/base64") + @APIResponse(responseCode = "200", description = "Document downloaded", content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM, schema = @Schema(type = SchemaType.STRING, format = "binary"))) + public Response ean13SvgBase64() throws IOException { + String image = Okapi.ean13Svg("123456789012+12345", 2.0); + return buildImageResponse(image, SVG_MIME_TYPE, "okapi-ean13.svg"); } private Code128 createCode128() { @@ -91,4 +89,4 @@ private Code128 createCode128() { barcode.setContent("123456789"); return barcode; } -} \ No newline at end of file +} diff --git a/ext-okapi/integration-tests/src/test/java/io/quarkiverse/barcode/it/OkapiResourceTest.java b/ext-okapi/integration-tests/src/test/java/io/quarkiverse/barcode/it/OkapiResourceTest.java index 94f4820..ff008a3 100644 --- a/ext-okapi/integration-tests/src/test/java/io/quarkiverse/barcode/it/OkapiResourceTest.java +++ b/ext-okapi/integration-tests/src/test/java/io/quarkiverse/barcode/it/OkapiResourceTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; import org.junit.jupiter.api.Test; @@ -38,4 +39,28 @@ public void testOkapiCode128Svg() { 123456789 123456789 """)); } + + @Test + public void testOkapiEan13Base64Png() { + given() + .when().get("/okapi/ean13/png/base64") + .then() + .statusCode(200) + .contentType(equalTo(BaseImageResource.PNG_MIME_TYPE)) + .header(CONTENT_LENGTH, Integer::parseInt, greaterThan(0)) + .body(startsWith( + "data:image/png;base64,77+9UE5HDQoaCgAAAA1JSERSAAABLAAAASwIAAAAAF")); + } + + @Test + public void testOkapiEan13Base64Svg() { + given() + .when().get("/okapi/ean13/svg/base64") + .then() + .statusCode(200) + .contentType(equalTo(BaseImageResource.SVG_MIME_TYPE)) + .header(CONTENT_LENGTH, Integer::parseInt, greaterThan(0)) + .body(is( + "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iCiAgICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHdpZHRoPSIzMDIiIGhlaWdodD0iMTAwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgIDxkZXNjPjEyMzQ1Njc4OTAxMjwvZGVzYz4KICAgPGcgaWQ9ImJhcmNvZGUiIGZpbGw9IiMwMDAwMDAiPgogICAgICA8cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMzAyIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI0ZGRkZGRiIgLz4KICAgICAgPHJlY3QgeD0iMC4wMCIgeT0iMC4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iOTAuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjQuMDAiIHk9IjAuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9IjkwLjAwIiAvPgogICAgICA8cmVjdCB4PSIxMC4wMCIgeT0iMC4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iODAuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjE2LjAwIiB5PSIwLjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMjIuMDAiIHk9IjAuMDAiIHdpZHRoPSI4LjAwIiBoZWlnaHQ9IjgwLjAwIiAvPgogICAgICA8cmVjdCB4PSIzMi4wMCIgeT0iMC4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iODAuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjM4LjAwIiB5PSIwLjAwIiB3aWR0aD0iNi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iNDYuMDAiIHk9IjAuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9IjgwLjAwIiAvPgogICAgICA8cmVjdCB4PSI1MC4wMCIgeT0iMC4wMCIgd2lkdGg9IjQuMDAiIGhlaWdodD0iODAuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjYwLjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iNzAuMDAiIHk9IjAuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9IjgwLjAwIiAvPgogICAgICA8cmVjdCB4PSI3NC4wMCIgeT0iMC4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iODAuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjgwLjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iODguMDAiIHk9IjAuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9IjgwLjAwIiAvPgogICAgICA8cmVjdCB4PSI5Mi4wMCIgeT0iMC4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iOTAuMDAiIC8+CiAgICAgIDxyZWN0IHg9Ijk2LjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI5MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTAwLjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTA2LjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTE0LjAwIiB5PSIwLjAwIiB3aWR0aD0iNi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTIyLjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTI4LjAwIiB5PSIwLjAwIiB3aWR0aD0iNi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTM4LjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTQyLjAwIiB5PSIwLjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTUwLjAwIiB5PSIwLjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTU2LjAwIiB5PSIwLjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTYyLjAwIiB5PSIwLjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTcwLjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTc2LjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI4MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTg0LjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI5MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMTg4LjAwIiB5PSIwLjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI5MC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMjA4LjAwIiB5PSIxNi4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iNzQuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjIxMi4wMCIgeT0iMTYuMDAiIHdpZHRoPSI0LjAwIiBoZWlnaHQ9Ijc0LjAwIiAvPgogICAgICA8cmVjdCB4PSIyMTguMDAiIHk9IjE2LjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI3NC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMjI2LjAwIiB5PSIxNi4wMCIgd2lkdGg9IjQuMDAiIGhlaWdodD0iNzQuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjIzMi4wMCIgeT0iMTYuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9Ijc0LjAwIiAvPgogICAgICA8cmVjdCB4PSIyMzguMDAiIHk9IjE2LjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI3NC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMjQ0LjAwIiB5PSIxNi4wMCIgd2lkdGg9IjQuMDAiIGhlaWdodD0iNzQuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjI1MC4wMCIgeT0iMTYuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9Ijc0LjAwIiAvPgogICAgICA8cmVjdCB4PSIyNTQuMDAiIHk9IjE2LjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI3NC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMjY0LjAwIiB5PSIxNi4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iNzQuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjI2OC4wMCIgeT0iMTYuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9Ijc0LjAwIiAvPgogICAgICA8cmVjdCB4PSIyNzIuMDAiIHk9IjE2LjAwIiB3aWR0aD0iMi4wMCIgaGVpZ2h0PSI3NC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMjgwLjAwIiB5PSIxNi4wMCIgd2lkdGg9IjQuMDAiIGhlaWdodD0iNzQuMDAiIC8+CiAgICAgIDxyZWN0IHg9IjI4Ni4wMCIgeT0iMTYuMDAiIHdpZHRoPSIyLjAwIiBoZWlnaHQ9Ijc0LjAwIiAvPgogICAgICA8cmVjdCB4PSIyOTAuMDAiIHk9IjE2LjAwIiB3aWR0aD0iNC4wMCIgaGVpZ2h0PSI3NC4wMCIgLz4KICAgICAgPHJlY3QgeD0iMzAwLjAwIiB5PSIxNi4wMCIgd2lkdGg9IjIuMDAiIGhlaWdodD0iNzQuMDAiIC8+CiAgICAgIDx0ZXh0IHg9Ii0xMC4wMCIgeT0iOTYuMDAiIHRleHQtYW5jaG9yPSJlbmQiCiAgICAgICAgIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTYuMDAiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAxCiAgICAgIDwvdGV4dD4KICAgICAgPHRleHQgeD0iMTAuMDAiIHk9Ijk2LjAwIiB0ZXh0LWFuY2hvcj0ic3RhcnQiCiAgICAgICAgIHRleHRMZW5ndGg9Ijc4LjAwIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciCiAgICAgICAgIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTYuMDAiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAyMzQ1NjcKICAgICAgPC90ZXh0PgogICAgICA8dGV4dCB4PSIxMDIuMDAiIHk9Ijk2LjAwIiB0ZXh0LWFuY2hvcj0ic3RhcnQiCiAgICAgICAgIHRleHRMZW5ndGg9Ijc4LjAwIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciCiAgICAgICAgIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTYuMDAiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICA4OTAxMjgKICAgICAgPC90ZXh0PgogICAgICA8dGV4dCB4PSIyNTUuMDAiIHk9IjEyLjAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIgogICAgICAgICBmb250LWZhbWlseT0iSGVsdmV0aWNhIiBmb250LXNpemU9IjE2LjAwIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgMTIzNDUKICAgICAgPC90ZXh0PgogICA8L2c+Cjwvc3ZnPgo=")); + } } \ No newline at end of file diff --git a/ext-okapi/runtime/src/main/java/io/quarkiverse/barcode/okapi/Okapi.java b/ext-okapi/runtime/src/main/java/io/quarkiverse/barcode/okapi/Okapi.java new file mode 100644 index 0000000..6b7b7e6 --- /dev/null +++ b/ext-okapi/runtime/src/main/java/io/quarkiverse/barcode/okapi/Okapi.java @@ -0,0 +1,361 @@ +package io.quarkiverse.barcode.okapi; + +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +import javax.imageio.ImageIO; + +import uk.org.okapibarcode.backend.Code128; +import uk.org.okapibarcode.backend.Code3Of9; +import uk.org.okapibarcode.backend.Code93; +import uk.org.okapibarcode.backend.DataMatrix; +import uk.org.okapibarcode.backend.Ean; +import uk.org.okapibarcode.backend.QrCode; +import uk.org.okapibarcode.backend.Symbol; +import uk.org.okapibarcode.backend.Upc; +import uk.org.okapibarcode.graphics.Color; +import uk.org.okapibarcode.output.Java2DRenderer; +import uk.org.okapibarcode.output.SvgRenderer; + +/** + * Utility class for generating various barcode formats as PNG or SVG images. + *

+ * This class provides methods to create barcodes in popular formats such as + * Code128, Code39, Code93, EAN-13, EAN-8, UPC-A, QR Code, and Data Matrix. + * Each barcode type can be generated as a {@link BufferedImage} (for PNG) + * or as a data URI string (in either PNG or SVG formats) that can be + * directly embedded into HTML or other documents. + *

+ *

+ * Example usage: + * + *

{@code
+ * // Generate a PNG BufferedImage for a Code128 barcode
+ * BufferedImage image = Okapi.code128("1234567890", 200, 100);
+ *
+ * // Generate a data URI for embedding a Code39 barcode in SVG format
+ * String svgDataUri = Okapi.code39Svg("12345", 1.0);
+ * }
+ *

+ *

+ * All methods are static, and this class is not intended to be instantiated. + * The methods are designed for easy generation of barcode images with adjustable + * dimensions (for PNG) or magnification (for SVG) to suit various rendering contexts. + *

+ * + *

+ * Supported barcode formats: + *

+ * + * + *

+ * Supported image outputs: + *

+ * + * + *

+ * All SVG generation methods offer an optional magnification parameter to scale + * the output image, making it easy to produce barcodes suitable for a wide range + * of display sizes and resolutions. + *

+ * + *

+ * Note: PNG generation relies on the {@link Java2DRenderer} + * and requires setting appropriate image dimensions for accurate rendering. + * SVG output uses {@link SvgRenderer} and supports magnification scaling. + *

+ * + * @see Code128 + * @see Code3Of9 + * @see Ean + * @see SvgRenderer + * @see Java2DRenderer + */ +public class Okapi { + + private Okapi() { + // prevent instantiation + } + + public static BufferedImage code128(String value, int width, int height) { + Code128 code128 = new Code128(); + code128.setContent(value); + return generateBarcodePng(code128, width, height); + } + + public static String code128Png(String value, int width, int height) { + return dataUriPng(code128(value, width, height)); + } + + public static String code128Svg(String value, double magnification) { + Code128 code128 = new Code128(); + code128.setContent(value); + return dataUriSvg(generateBarcodeSvg(code128, magnification)); + } + + public static BufferedImage code39(String value, int width, int height) { + Code3Of9 code39 = new Code3Of9(); + code39.setContent(value); + return generateBarcodePng(code39, width, height); + } + + public static String code39Png(String value, int width, int height) { + return dataUriPng(code39(value, width, height)); + } + + public static String code39Svg(String value, double magnification) { + Code3Of9 code39 = new Code3Of9(); + code39.setContent(value); + return dataUriSvg(generateBarcodeSvg(code39, magnification)); + } + + public static BufferedImage code93(String value, int width, int height) { + Code93 code93 = new Code93(); + code93.setContent(value); + return generateBarcodePng(code93, width, height); + } + + public static String code93Png(String value, int width, int height) { + return dataUriPng(code93(value, width, height)); + } + + public static String code93Svg(String value, double magnification) { + Code93 code93 = new Code93(); + code93.setContent(value); + return dataUriSvg(generateBarcodeSvg(code93, magnification)); + } + + public static BufferedImage ean13(String value, int width, int height) { + Ean ean13 = new Ean(Ean.Mode.EAN13); + ean13.setContent(value); + return generateBarcodePng(ean13, width, height); + } + + public static String ean13Png(String value, int width, int height) { + return dataUriPng(ean13(value, width, height)); + } + + public static String ean13Svg(String value, double magnification) { + Ean ean13 = new Ean(Ean.Mode.EAN13); + ean13.setContent(value); + return dataUriSvg(generateBarcodeSvg(ean13, magnification)); + } + + public static BufferedImage ean8(String value, int width, int height) { + Ean ean8 = new Ean(Ean.Mode.EAN8); + ean8.setContent(value); + return generateBarcodePng(ean8, width, height); + } + + public static String ean8Png(String value, int width, int height) { + return dataUriPng(ean8(value, width, height)); + } + + public static String ean8Svg(String value, double magnification) { + Ean ean8 = new Ean(Ean.Mode.EAN8); + ean8.setContent(value); + return dataUriSvg(generateBarcodeSvg(ean8, magnification)); + } + + public static BufferedImage upcA(String value, int width, int height) { + Upc upcA = new Upc(Upc.Mode.UPCA); + upcA.setContent(value); + return generateBarcodePng(upcA, width, height); + } + + public static String upcAPng(String value, int width, int height) { + return dataUriPng(upcA(value, width, height)); + } + + public static String upcASvg(String value, double magnification) { + Upc upcA = new Upc(Upc.Mode.UPCA); + upcA.setContent(value); + return dataUriSvg(generateBarcodeSvg(upcA, magnification)); + } + + public static BufferedImage qrCode(String value, int width, int height) { + QrCode qrCode = new QrCode(); + qrCode.setContent(value); + return generateBarcodePng(qrCode, width, height); + } + + public static String qrCodePng(String value, int width, int height) { + return dataUriPng(qrCode(value, width, height)); + } + + public static String qrCodeSvg(String value, double magnification) { + QrCode qrCode = new QrCode(); + qrCode.setContent(value); + return dataUriSvg(generateBarcodeSvg(qrCode, magnification)); + } + + public static BufferedImage dataMatrix(String value, int width, int height) { + DataMatrix dataMatrix = new DataMatrix(); + dataMatrix.setContent(value); + return generateBarcodePng(dataMatrix, width, height); + } + + public static String dataMatrixPng(String value, int width, int height) { + return dataUriPng(dataMatrix(value, width, height)); + } + + public static String dataMatrixSvg(String value, double magnification) { + DataMatrix dataMatrix = new DataMatrix(); + dataMatrix.setContent(value); + return dataUriSvg(generateBarcodeSvg(dataMatrix, magnification)); + } + + /** + * Generates an SVG barcode as a String with default magnification of 1.0. + * + * @param symbol The barcode symbol to render + * @return String containing the SVG barcode + */ + public static String generateBarcodeSvgAsString(Symbol symbol) { + return new String(generateBarcodeSvg(symbol, 1.0)); + } + + /** + * Generates an SVG barcode as a byte array with default magnification of 1.0. + * + * @param symbol The barcode symbol to render + * @return byte array containing the SVG barcode + */ + public static byte[] generateBarcodeSvg(Symbol symbol) { + return generateBarcodeSvg(symbol, 1.0); + } + + /** + * Generates an SVG barcode as a byte array with specified magnification. + * + * @param symbol The barcode symbol to render + * @param magnification The magnification factor to apply + * @return byte array containing the SVG barcode + */ + public static byte[] generateBarcodeSvg(Symbol symbol, double magnification) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + SvgRenderer renderer = new SvgRenderer(out, magnification, Color.WHITE, Color.BLACK, true); + try { + renderer.render(symbol); + } catch (IOException e) { + throw new RuntimeException(e); + } + return out.toByteArray(); + } + + /** + * Generates a PNG barcode as a BufferedImage with default magnification of 1.0. + * + * @param symbol The barcode symbol to render + * @param width The desired width of the image + * @param height The desired height of the image + * @return BufferedImage containing the PNG barcode + */ + public static BufferedImage generateBarcodePng(Symbol symbol, int width, int height) { + return generateBarcodePng(symbol, width, height, 1.0); + } + + /** + * Generates a PNG barcode as a BufferedImage with specified magnification. + * + * @param symbol The barcode symbol to render + * @param width The desired width of the image + * @param height The desired height of the image + * @param magnification The magnification factor to apply + * @return BufferedImage containing the PNG barcode + */ + public static BufferedImage generateBarcodePng(Symbol symbol, int width, int height, double magnification) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g2d = image.createGraphics(); + Java2DRenderer renderer = new Java2DRenderer(g2d, magnification, Color.WHITE, Color.BLACK); + renderer.render(symbol); + g2d.dispose(); + return image; + } + + /** + * Converts a BufferedImage to a data URI string in PNG format. + * + * @param image The BufferedImage to convert + * @return String containing the data URI + */ + public static String dataUriPng(BufferedImage image) { + return base64ToDataUri(pngToBase64(image), "image/png"); + } + + /** + * Converts a byte array to a data URI string in SVG format. + * + * @param bytes The byte array containing SVG data + * @return String containing the data URI + */ + public static String dataUriSvg(byte[] bytes) { + final String base64 = bytesToBase64(bytes); + return base64ToDataUri(base64, "image/svg+xml"); + } + + /** + * Converts a BufferedImage to a PNG byte array. + * + * @param image The BufferedImage to convert + * @return byte array containing the PNG data + * @throws RuntimeException if the image cannot be written + */ + public static byte[] pngToBytes(BufferedImage image) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + ImageIO.write(image, "png", out); + } catch (IOException e) { + throw new RuntimeException(e); + } + return out.toByteArray(); + } + + /** + * Converts a BufferedImage to a Base64 encoded PNG string. + * + * @param image The BufferedImage to convert + * @return String containing the Base64 encoded PNG data + */ + public static String pngToBase64(BufferedImage image) { + return bytesToBase64(new String(pngToBytes(image), StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8)); + } + + /** + * Converts a byte array to a Base64 encoded string. + * + * @param bytes The byte array to convert + * @return String containing the Base64 encoded data + */ + public static String bytesToBase64(byte[] bytes) { + return Base64.getEncoder().encodeToString(bytes); + } + + /** + * Creates a data URI string from Base64 encoded data and MIME type. + * + * @param base64 The Base64 encoded data + * @param mimeType The MIME type of the data + * @return String containing the data URI + */ + public static String base64ToDataUri(String base64, String mimeType) { + return "data:%s;base64,%s".formatted(mimeType, base64); + } + +} \ No newline at end of file diff --git a/ext-zxing/runtime/src/main/java/io/quarkiverse/barcode/zxing/ZebraCrossing.java b/ext-zxing/runtime/src/main/java/io/quarkiverse/barcode/zxing/ZebraCrossing.java index 3726977..185c901 100644 --- a/ext-zxing/runtime/src/main/java/io/quarkiverse/barcode/zxing/ZebraCrossing.java +++ b/ext-zxing/runtime/src/main/java/io/quarkiverse/barcode/zxing/ZebraCrossing.java @@ -6,11 +6,19 @@ import java.util.Base64; import java.util.Map; -import com.google.zxing.*; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.datamatrix.DataMatrixWriter; -import com.google.zxing.oned.*; +import com.google.zxing.oned.Code128Writer; +import com.google.zxing.oned.Code39Writer; +import com.google.zxing.oned.Code93Writer; +import com.google.zxing.oned.EAN13Writer; +import com.google.zxing.oned.EAN8Writer; +import com.google.zxing.oned.UPCAWriter; +import com.google.zxing.oned.UPCEWriter; import com.google.zxing.qrcode.QRCodeWriter; /** @@ -182,10 +190,23 @@ public static String dataMatrixImg(String value, int width, int height) { return dataUriImg(dataMatrix(value, width, height)); } + /** + * Converts a BitMatrix barcode to an HTML img tag with embedded PNG data URI. + * + * @param encoded The BitMatrix containing the barcode data + * @return String containing an HTML img tag with the barcode as a data URI + */ public static String dataUriImg(BitMatrix encoded) { return dataUriImg(base64ToDataUri(pngToBase64(barcodetoPng(encoded)))); } + /** + * Converts a BitMatrix barcode to a PNG byte array. + * + * @param encoded The BitMatrix containing the barcode data + * @return byte array containing the PNG data + * @throws RuntimeException if the image cannot be written + */ public static byte[] barcodetoPng(BitMatrix encoded) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { @@ -196,18 +217,42 @@ public static byte[] barcodetoPng(BitMatrix encoded) { return out.toByteArray(); } + /** + * Converts a PNG byte array to a Base64 encoded string. + * + * @param png The PNG byte array to convert + * @return String containing the Base64 encoded PNG data + */ public static String pngToBase64(byte[] png) { return Base64.getEncoder().encodeToString(png); } + /** + * Creates a PNG data URI string from Base64 encoded data. + * + * @param base64 The Base64 encoded PNG data + * @return String containing the PNG data URI + */ public static String base64ToDataUri(String base64) { return "data:image/png;base64," + base64; } + /** + * Wraps a data URI in an HTML img tag. + * + * @param dataUri The data URI to wrap + * @return String containing an HTML img tag with the data URI as the src + */ public static String dataUriImg(String dataUri) { return ""; } + /** + * Gets the default encoding hints used for barcode generation. + * Sets UTF-8 as the default character set. + * + * @return Map containing the default encoding hints + */ private static Map getHints() { return Map.of(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name()); }