A comprehensive guide to generating various barcode types in Java, including 1D and 2D barcodes for different use cases.
Overview of Barcode Types
1D Barcodes:
- Code 128 - High-density alphanumeric
- Code 39 - Alphanumeric, widely used
- EAN-13 - Product numbering (12 numeric digits + check digit)
- EAN-8 - Product numbering (7 numeric digits + check digit)
- UPC-A - US product numbering
- ITF-14 - Shipping cartons
- CODABAR - Libraries, blood banks
2D Barcodes:
- QR Code - High capacity, popular for mobile
- Data Matrix - Small size, high density
- PDF417 - Portable data files
- Aztec Code - No quiet zone required
Dependencies and Setup
1. Maven Dependencies
<properties>
<barbecue.version>1.5-beta1</barbecue.version>
<zxing.version>3.5.1</zxing.version>
<barcode4j.version>2.1.0</barcode4j.version>
<spring-boot.version>3.1.0</spring-boot.version>
<apache-pdfbox.version>2.0.29</apache-pdfbox.version>
</properties>
<dependencies>
<!-- Barbecue - 1D Barcodes -->
<dependency>
<groupId>net.sourceforge.barbecue</groupId>
<artifactId>barbecue</artifactId>
<version>${barbecue.version}</version>
</dependency>
<!-- ZXing - 2D Barcodes & QR Codes -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>${zxing.version}</version>
</dependency>
<!-- Barcode4J - Professional 1D/2D -->
<dependency>
<groupId>net.sf.barcode4j</groupId>
<artifactId>barcode4j</artifactId>
<version>${barcode4j.version}</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Image Processing -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>${apache-pdfbox.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
2. Configuration Classes
@Configuration
@ConfigurationProperties(prefix = "barcode")
@Data
public class BarcodeConfig {
private int width = 300;
private int height = 150;
private String imageFormat = "PNG";
private int qrCodeSize = 250;
private String outputDirectory = "./generated-barcodes/";
private boolean includeText = true;
private String fontName = "Arial";
private int fontSize = 12;
@PostConstruct
public void init() {
// Create output directory if it doesn't exist
File outputDir = new File(outputDirectory);
if (!outputDir.exists()) {
outputDir.mkdirs();
}
}
}
@Configuration
public class BarcodeBeanConfig {
@Bean
public BarcodeFormat barcodeFormat() {
return BarcodeFormat.QR_CODE;
}
}
Core Barcode Service Implementation
1. Barcode Types Enum
public enum BarcodeType {
// 1D Barcodes
CODE_128,
CODE_39,
EAN_13,
EAN_8,
UPC_A,
UPC_E,
ITF_14,
CODABAR,
// 2D Barcodes
QR_CODE,
DATA_MATRIX,
PDF_417,
AZTEC_CODE
}
public enum ImageFormat {
PNG,
JPEG,
GIF,
SVG,
PDF
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BarcodeRequest {
@NotBlank
private String data;
@NotNull
private BarcodeType barcodeType;
private ImageFormat imageFormat;
private Integer width;
private Integer height;
private String fileName;
private boolean includeText;
private String textPosition; // TOP, BOTTOM
private Integer fontSize;
private String foregroundColor;
private String backgroundColor;
private Map<String, Object> additionalParams;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BarcodeResponse {
private boolean success;
private String message;
private String filePath;
private byte[] imageData;
private String base64Image;
private String mimeType;
private Long fileSize;
private LocalDateTime generatedAt;
}
2. Main Barcode Service Interface
public interface BarcodeService {
BarcodeResponse generateBarcode(BarcodeRequest request);
BarcodeResponse generateBarcodeToFile(BarcodeRequest request);
byte[] generateBarcodeAsBytes(BarcodeRequest request);
String generateBarcodeAsBase64(BarcodeRequest request);
boolean validateBarcodeData(BarcodeType type, String data);
List<BarcodeType> getSupportedBarcodeTypes();
}
@Service
@Slf4j
public class BarcodeServiceImpl implements BarcodeService {
private final BarcodeConfig config;
private final ObjectMapper objectMapper;
private static final Map<BarcodeType, BarcodeFormat> ZXING_FORMAT_MAP = Map.of(
BarcodeType.QR_CODE, BarcodeFormat.QR_CODE,
BarcodeType.DATA_MATRIX, BarcodeFormat.DATA_MATRIX,
BarcodeType.PDF_417, BarcodeFormat.PDF_417,
BarcodeType.AZTEC_CODE, BarcodeFormat.AZTEC_CODE,
BarcodeType.CODE_128, BarcodeFormat.CODE_128,
BarcodeType.CODE_39, BarcodeFormat.CODE_39,
BarcodeType.EAN_13, BarcodeFormat.EAN_13,
BarcodeType.EAN_8, BarcodeFormat.EAN_8,
BarcodeType.UPC_A, BarcodeFormat.UPC_A,
BarcodeType.UPC_E, BarcodeFormat.UPC_E,
BarcodeType.ITF_14, BarcodeFormat.ITF_14
);
public BarcodeServiceImpl(BarcodeConfig config, ObjectMapper objectMapper) {
this.config = config;
this.objectMapper = objectMapper;
}
@Override
public BarcodeResponse generateBarcode(BarcodeRequest request) {
try {
validateRequest(request);
byte[] imageData = generateBarcodeAsBytes(request);
String base64Image = Base64.getEncoder().encodeToString(imageData);
return BarcodeResponse.builder()
.success(true)
.message("Barcode generated successfully")
.imageData(imageData)
.base64Image(base64Image)
.mimeType(getMimeType(request.getImageFormat()))
.generatedAt(LocalDateTime.now())
.build();
} catch (Exception e) {
log.error("Failed to generate barcode: {}", e.getMessage(), e);
return BarcodeResponse.builder()
.success(false)
.message("Failed to generate barcode: " + e.getMessage())
.generatedAt(LocalDateTime.now())
.build();
}
}
@Override
public BarcodeResponse generateBarcodeToFile(BarcodeRequest request) {
try {
validateRequest(request);
String fileName = generateFileName(request);
String filePath = config.getOutputDirectory() + fileName;
byte[] imageData = generateBarcodeAsBytes(request);
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(imageData);
}
File file = new File(filePath);
return BarcodeResponse.builder()
.success(true)
.message("Barcode saved to file successfully")
.filePath(filePath)
.fileSize(file.length())
.mimeType(getMimeType(request.getImageFormat()))
.generatedAt(LocalDateTime.now())
.build();
} catch (Exception e) {
log.error("Failed to generate barcode to file: {}", e.getMessage(), e);
return BarcodeResponse.builder()
.success(false)
.message("Failed to generate barcode to file: " + e.getMessage())
.generatedAt(LocalDateTime.now())
.build();
}
}
@Override
public byte[] generateBarcodeAsBytes(BarcodeRequest request) {
try {
switch (request.getBarcodeType()) {
case QR_CODE:
case DATA_MATRIX:
case PDF_417:
case AZTEC_CODE:
return generate2DBarcode(request);
case CODE_128:
case CODE_39:
case EAN_13:
case EAN_8:
case UPC_A:
case UPC_E:
case ITF_14:
case CODABAR:
return generate1DBarcode(request);
default:
throw new UnsupportedBarcodeTypeException(
"Unsupported barcode type: " + request.getBarcodeType());
}
} catch (Exception e) {
throw new BarcodeGenerationException("Failed to generate barcode as bytes", e);
}
}
@Override
public String generateBarcodeAsBase64(BarcodeRequest request) {
byte[] imageData = generateBarcodeAsBytes(request);
return Base64.getEncoder().encodeToString(imageData);
}
@Override
public boolean validateBarcodeData(BarcodeType type, String data) {
try {
switch (type) {
case EAN_13:
return validateEAN13(data);
case EAN_8:
return validateEAN8(data);
case UPC_A:
return validateUPCA(data);
case UPC_E:
return validateUPCE(data);
case CODE_39:
return validateCode39(data);
default:
return data != null && !data.trim().isEmpty();
}
} catch (Exception e) {
return false;
}
}
@Override
public List<BarcodeType> getSupportedBarcodeTypes() {
return Arrays.asList(BarcodeType.values());
}
// Private implementation methods
private byte[] generate2DBarcode(BarcodeRequest request) throws Exception {
BarcodeFormat format = ZXING_FORMAT_MAP.get(request.getBarcodeType());
if (format == null) {
throw new UnsupportedBarcodeTypeException("Unsupported 2D barcode type: " + request.getBarcodeType());
}
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.MARGIN, 1);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
// Add error correction for QR codes
if (request.getBarcodeType() == BarcodeType.QR_CODE) {
hints.put(EncodeHintType.ERROR_CORRECTION, com.google.zxing.qrcode.decoder.ErrorCorrectionLevel.M);
}
BitMatrix bitMatrix = new MultiFormatWriter().encode(
request.getData(),
format,
request.getWidth() != null ? request.getWidth() : config.getWidth(),
request.getHeight() != null ? request.getHeight() : config.getHeight(),
hints
);
return convertBitMatrixToImage(bitMatrix, request);
}
private byte[] generate1DBarcode(BarcodeRequest request) throws Exception {
switch (request.getBarcodeType()) {
case CODE_128:
return generateCode128Barcode(request);
case CODE_39:
return generateCode39Barcode(request);
case EAN_13:
return generateEAN13Barcode(request);
case EAN_8:
return generateEAN8Barcode(request);
case UPC_A:
return generateUPCABarcode(request);
default:
return generateBarcode4JBarcode(request);
}
}
private byte[] generateCode128Barcode(BarcodeRequest request) throws Exception {
Barcode barcode = BarcodeFactory.createCode128(request.getData());
configureBarcodeAppearance(barcode, request);
return writeBarcodeToBytes(barcode, request);
}
private byte[] generateCode39Barcode(BarcodeRequest request) throws Exception {
Barcode barcode = BarcodeFactory.createCode39(request.getData(), false);
configureBarcodeAppearance(barcode, request);
return writeBarcodeToBytes(barcode, request);
}
private byte[] generateEAN13Barcode(BarcodeRequest request) throws Exception {
if (!validateEAN13(request.getData())) {
throw new InvalidBarcodeDataException("Invalid EAN-13 data: " + request.getData());
}
Barcode barcode = BarcodeFactory.createEAN13(request.getData());
configureBarcodeAppearance(barcode, request);
return writeBarcodeToBytes(barcode, request);
}
private byte[] generateEAN8Barcode(BarcodeRequest request) throws Exception {
if (!validateEAN8(request.getData())) {
throw new InvalidBarcodeDataException("Invalid EAN-8 data: " + request.getData());
}
Barcode barcode = BarcodeFactory.createEAN8(request.getData());
configureBarcodeAppearance(barcode, request);
return writeBarcodeToBytes(barcode, request);
}
private byte[] generateUPCABarcode(BarcodeRequest request) throws Exception {
if (!validateUPCA(request.getData())) {
throw new InvalidBarcodeDataException("Invalid UPC-A data: " + request.getData());
}
Barcode barcode = BarcodeFactory.createUPCA(request.getData());
configureBarcodeAppearance(barcode, request);
return writeBarcodeToBytes(barcode, request);
}
private byte[] generateBarcode4JBarcode(BarcodeRequest request) throws Exception {
BarcodeUtil util = BarcodeUtil.getInstance();
CanvasProvider canvasProvider = new BitmapCanvasProvider(
request.getWidth() != null ? request.getWidth() : config.getWidth(),
request.getHeight() != null ? request.getHeight() : config.getHeight(),
getMimeType(request.getImageFormat()),
BufferedImage.TYPE_BYTE_BINARY,
true,
0
);
String symbology = getBarcode4JSymbology(request.getBarcodeType());
BarcodeGenerator generator = util.createBarcodeGenerator(symbology);
generator.generateBarcode(canvasProvider, request.getData());
canvasProvider.finish();
if (canvasProvider instanceof BitmapCanvasProvider) {
BufferedImage image = ((BitmapCanvasProvider) canvasProvider).getBufferedImage();
return convertImageToBytes(image, request.getImageFormat());
}
throw new BarcodeGenerationException("Failed to generate barcode using Barcode4J");
}
private void configureBarcodeAppearance(Barcode barcode, BarcodeRequest request) {
barcode.setBarWidth(2);
barcode.setDrawingText(request.isIncludeText());
if (request.getWidth() != null) {
barcode.setPreferredWidth(request.getWidth());
}
if (request.getHeight() != null) {
barcode.setPreferredHeight(request.getHeight());
}
// Set colors if provided
if (request.getForegroundColor() != null) {
try {
barcode.setForeground(Color.decode(request.getForegroundColor()));
} catch (Exception e) {
log.warn("Invalid foreground color: {}", request.getForegroundColor());
}
}
if (request.getBackgroundColor() != null) {
try {
barcode.setBackground(Color.decode(request.getBackgroundColor()));
} catch (Exception e) {
log.warn("Invalid background color: {}", request.getBackgroundColor());
}
}
}
private byte[] writeBarcodeToBytes(Barcode barcode, BarcodeRequest request) throws Exception {
BufferedImage image = BarcodeImageHandler.getImage(barcode);
return convertImageToBytes(image, request.getImageFormat());
}
private byte[] convertBitMatrixToImage(BitMatrix matrix, BarcodeRequest request) throws Exception {
BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix);
// Add text below barcode if requested
if (request.isIncludeText() && request.getData() != null) {
image = addTextToImage(image, request.getData(), request);
}
return convertImageToBytes(image, request.getImageFormat());
}
private BufferedImage addTextToImage(BufferedImage originalImage, String text, BarcodeRequest request) {
int width = originalImage.getWidth();
int height = originalImage.getHeight();
// Create new image with extra space for text
int textHeight = 20;
BufferedImage newImage = new BufferedImage(width, height + textHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newImage.createGraphics();
// Set background
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, width, height + textHeight);
// Draw original barcode
g2d.drawImage(originalImage, 0, 0, null);
// Draw text
g2d.setColor(Color.BLACK);
g2d.setFont(new Font(
request.getFontName() != null ? request.getFontName() : config.getFontName(),
Font.PLAIN,
request.getFontSize() != null ? request.getFontSize() : config.getFontSize()
));
FontMetrics metrics = g2d.getFontMetrics();
int textWidth = metrics.stringWidth(text);
int x = (width - textWidth) / 2;
int y = height + 15;
g2d.drawString(text, x, y);
g2d.dispose();
return newImage;
}
private byte[] convertImageToBytes(BufferedImage image, ImageFormat format) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String formatName = format != null ? format.name() : config.getImageFormat();
if (!ImageIO.write(image, formatName, baos)) {
throw new BarcodeGenerationException("No appropriate image writer found for format: " + formatName);
}
return baos.toByteArray();
}
// Validation methods
private boolean validateEAN13(String data) {
if (data == null || data.length() != 13 || !data.matches("\\d+")) {
return false;
}
return validateCheckDigit(data);
}
private boolean validateEAN8(String data) {
if (data == null || data.length() != 8 || !data.matches("\\d+")) {
return false;
}
return validateCheckDigit(data);
}
private boolean validateUPCA(String data) {
if (data == null || data.length() != 12 || !data.matches("\\d+")) {
return false;
}
return validateCheckDigit(data);
}
private boolean validateUPCE(String data) {
return data != null && data.length() == 8 && data.matches("\\d+");
}
private boolean validateCode39(String data) {
if (data == null || data.isEmpty()) return false;
return data.matches("[A-Z0-9\\-\\+\\.\\$/%\\s]+");
}
private boolean validateCheckDigit(String data) {
try {
int sum = 0;
for (int i = 0; i < data.length() - 1; i++) {
int digit = Character.getNumericValue(data.charAt(i));
sum += (i % 2 == 0) ? digit : digit * 3;
}
int checkDigit = (10 - (sum % 10)) % 10;
return checkDigit == Character.getNumericValue(data.charAt(data.length() - 1));
} catch (Exception e) {
return false;
}
}
private void validateRequest(BarcodeRequest request) {
if (request == null) {
throw new IllegalArgumentException("BarcodeRequest cannot be null");
}
if (request.getData() == null || request.getData().trim().isEmpty()) {
throw new InvalidBarcodeDataException("Barcode data cannot be null or empty");
}
if (request.getBarcodeType() == null) {
throw new IllegalArgumentException("Barcode type cannot be null");
}
if (!validateBarcodeData(request.getBarcodeType(), request.getData())) {
throw new InvalidBarcodeDataException(
"Invalid data for barcode type " + request.getBarcodeType() + ": " + request.getData());
}
}
private String generateFileName(BarcodeRequest request) {
String baseName = request.getFileName() != null ?
request.getFileName() :
"barcode_" + request.getBarcodeType().name().toLowerCase() + "_" + System.currentTimeMillis();
String extension = getFileExtension(request.getImageFormat());
return baseName + "." + extension;
}
private String getFileExtension(ImageFormat format) {
if (format == null) {
return config.getImageFormat().toLowerCase();
}
return format.name().toLowerCase();
}
private String getMimeType(ImageFormat format) {
if (format == null) {
return "image/png";
}
switch (format) {
case JPEG: return "image/jpeg";
case GIF: return "image/gif";
case SVG: return "image/svg+xml";
case PDF: return "application/pdf";
default: return "image/png";
}
}
private String getBarcode4JSymbology(BarcodeType type) {
switch (type) {
case CODE_128: return "code128";
case CODE_39: return "code39";
case EAN_13: return "ean-13";
case EAN_8: return "ean-8";
case UPC_A: return "upc-a";
case UPC_E: return "upc-e";
case ITF_14: return "itf-14";
case CODABAR: return "codabar";
case PDF_417: return "pdf417";
case DATA_MATRIX: return "datamatrix";
default: throw new UnsupportedBarcodeTypeException("Unsupported Barcode4J type: " + type);
}
}
}
3. Custom Exceptions
public class BarcodeGenerationException extends RuntimeException {
public BarcodeGenerationException(String message) {
super(message);
}
public BarcodeGenerationException(String message, Throwable cause) {
super(message, cause);
}
}
public class UnsupportedBarcodeTypeException extends BarcodeGenerationException {
public UnsupportedBarcodeTypeException(String message) {
super(message);
}
}
public class InvalidBarcodeDataException extends BarcodeGenerationException {
public InvalidBarcodeDataException(String message) {
super(message);
}
}
Advanced Barcode Features
1. Batch Barcode Generation
@Service
@Slf4j
public class BatchBarcodeService {
private final BarcodeService barcodeService;
private final BarcodeConfig config;
@Async
public CompletableFuture<BatchBarcodeResult> generateBatchBarcodes(List<BarcodeRequest> requests) {
List<BarcodeResponse> results = new ArrayList<>();
int successCount = 0;
int failureCount = 0;
for (BarcodeRequest request : requests) {
try {
BarcodeResponse response = barcodeService.generateBarcodeToFile(request);
results.add(response);
if (response.isSuccess()) {
successCount++;
} else {
failureCount++;
}
} catch (Exception e) {
log.error("Failed to generate barcode for request: {}", request, e);
failureCount++;
results.add(BarcodeResponse.builder()
.success(false)
.message("Generation failed: " + e.getMessage())
.build());
}
}
return CompletableFuture.completedFuture(
BatchBarcodeResult.builder()
.totalRequests(requests.size())
.successCount(successCount)
.failureCount(failureCount)
.results(results)
.completedAt(LocalDateTime.now())
.build()
);
}
public String generateBarcodesAsZip(List<BarcodeRequest> requests, String zipFileName) throws IOException {
String tempDir = config.getOutputDirectory() + "temp/" + System.currentTimeMillis() + "/";
new File(tempDir).mkdirs();
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos)) {
for (int i = 0; i < requests.size(); i++) {
BarcodeRequest request = requests.get(i);
try {
byte[] barcodeData = barcodeService.generateBarcodeAsBytes(request);
String fileName = request.getFileName() != null ?
request.getFileName() :
"barcode_" + (i + 1) + "." + getFileExtension(request.getImageFormat());
ZipEntry entry = new ZipEntry(fileName);
zos.putNextEntry(entry);
zos.write(barcodeData);
zos.closeEntry();
} catch (Exception e) {
log.warn("Failed to generate barcode for entry {}: {}", i, e.getMessage());
}
}
zos.finish();
return Base64.getEncoder().encodeToString(baos.toByteArray());
} finally {
// Cleanup temp directory
deleteDirectory(new File(tempDir));
}
}
private void deleteDirectory(File directory) {
if (directory.exists()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteDirectory(file);
} else {
file.delete();
}
}
}
directory.delete();
}
}
private String getFileExtension(ImageFormat format) {
return format != null ? format.name().toLowerCase() : "png";
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class BatchBarcodeResult {
private int totalRequests;
private int successCount;
private int failureCount;
private List<BarcodeResponse> results;
private LocalDateTime completedAt;
public double getSuccessRate() {
return totalRequests > 0 ? (double) successCount / totalRequests * 100 : 0;
}
}
2. Barcode Validation Service
@Service
@Slf4j
public class BarcodeValidationService {
public ValidationResult validateBarcode(BarcodeType type, String data) {
ValidationResult result = new ValidationResult();
result.setBarcodeType(type);
result.setData(data);
result.setValidatedAt(LocalDateTime.now());
try {
switch (type) {
case EAN_13:
result.setValid(validateEAN13Structure(data));
if (result.isValid()) {
result.setCheckDigit(calculateEAN13CheckDigit(data));
}
break;
case EAN_8:
result.setValid(validateEAN8Structure(data));
if (result.isValid()) {
result.setCheckDigit(calculateEAN8CheckDigit(data));
}
break;
case UPC_A:
result.setValid(validateUPCAStructure(data));
if (result.isValid()) {
result.setCheckDigit(calculateUPCACheckDigit(data));
}
break;
case CODE_128:
result.setValid(validateCode128Structure(data));
break;
case CODE_39:
result.setValid(validateCode39Structure(data));
break;
default:
result.setValid(data != null && !data.trim().isEmpty());
result.setMessage("Basic validation only");
}
if (!result.isValid()) {
result.setMessage("Invalid barcode data structure");
}
} catch (Exception e) {
result.setValid(false);
result.setMessage("Validation error: " + e.getMessage());
}
return result;
}
public List<ValidationResult> validateBarcodeBatch(List<BarcodeValidationRequest> requests) {
return requests.stream()
.map(req -> validateBarcode(req.getBarcodeType(), req.getData()))
.collect(Collectors.toList());
}
private boolean validateEAN13Structure(String data) {
return data != null && data.matches("\\d{13}") && validateCheckDigit(data);
}
private boolean validateEAN8Structure(String data) {
return data != null && data.matches("\\d{8}") && validateCheckDigit(data);
}
private boolean validateUPCAStructure(String data) {
return data != null && data.matches("\\d{12}") && validateCheckDigit(data);
}
private boolean validateCode128Structure(String data) {
return data != null && !data.isEmpty() && data.matches("[\\x00-\\x7F]+");
}
private boolean validateCode39Structure(String data) {
return data != null && !data.isEmpty() && data.matches("[A-Z0-9\\-\\+\\.\\$/%\\s]+");
}
private String calculateEAN13CheckDigit(String data) {
return calculateCheckDigit(data.substring(0, 12));
}
private String calculateEAN8CheckDigit(String data) {
return calculateCheckDigit(data.substring(0, 7));
}
private String calculateUPCACheckDigit(String data) {
return calculateCheckDigit(data.substring(0, 11));
}
private String calculateCheckDigit(String data) {
int sum = 0;
for (int i = 0; i < data.length(); i++) {
int digit = Character.getNumericValue(data.charAt(i));
sum += (i % 2 == 0) ? digit * 3 : digit;
}
int checkDigit = (10 - (sum % 10)) % 10;
return String.valueOf(checkDigit);
}
private boolean validateCheckDigit(String data) {
String payload = data.substring(0, data.length() - 1);
String expectedCheckDigit = calculateCheckDigit(payload);
String actualCheckDigit = data.substring(data.length() - 1);
return expectedCheckDigit.equals(actualCheckDigit);
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class ValidationResult {
private BarcodeType barcodeType;
private String data;
private boolean isValid;
private String message;
private String checkDigit;
private LocalDateTime validatedAt;
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class BarcodeValidationRequest {
private BarcodeType barcodeType;
private String data;
}
3. Barcode PDF Generation
@Service
@Slf4j
public class BarcodePDFService {
private final BarcodeService barcodeService;
public byte[] generateBarcodePDF(BarcodePDFRequest request) throws IOException {
try (PDDocument document = new PDDocument();
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
float margin = 50;
float yPosition = page.getMediaBox().getHeight() - margin;
float maxWidth = page.getMediaBox().getWidth() - 2 * margin;
// Add title
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 16);
contentStream.beginText();
contentStream.newLineAtOffset(margin, yPosition);
contentStream.showText("Barcode Labels");
contentStream.endText();
yPosition -= 30;
// Generate barcodes
for (int i = 0; i < request.getBarcodes().size(); i++) {
BarcodeRequest barcodeRequest = request.getBarcodes().get(i);
if (i > 0 && i % 3 == 0) {
yPosition -= 150; // Move to next row
}
float xPosition = margin + (i % 3) * (maxWidth / 3);
// Generate barcode image
byte[] barcodeImage = barcodeService.generateBarcodeAsBytes(barcodeRequest);
// Add barcode to PDF
addBarcodeToPDF(contentStream, barcodeImage, xPosition, yPosition, barcodeRequest.getData());
}
}
document.save(baos);
return baos.toByteArray();
}
}
private void addBarcodeToPDF(PDPageContentStream contentStream, byte[] barcodeImage,
float x, float y, String label) throws IOException {
// Convert byte array to PDImageXObject
PDImageXObject pdImage = PDImageXObject.createFromByteArray(
contentStream.getDocument(), barcodeImage, "barcode");
// Draw barcode image
float width = 100;
float height = 50;
contentStream.drawImage(pdImage, x, y - height, width, height);
// Add label
contentStream.setFont(PDType1Font.HELVETICA, 8);
contentStream.beginText();
contentStream.newLineAtOffset(x, y - height - 15);
contentStream.showText(label);
contentStream.endText();
}
public byte[] generateBarcodeSheet(List<BarcodeRequest> barcodes, String sheetTitle) throws IOException {
BarcodePDFRequest request = BarcodePDFRequest.builder()
.barcodes(barcodes)
.title(sheetTitle)
.build();
return generateBarcodePDF(request);
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class BarcodePDFRequest {
private String title;
private List<BarcodeRequest> barcodes;
private boolean includeLabels = true;
private String footerText;
}
REST Controllers
1. Barcode Generation Controller
@RestController
@RequestMapping("/api/barcodes")
@Validated
@Slf4j
public class BarcodeController {
private final BarcodeService barcodeService;
private final BatchBarcodeService batchBarcodeService;
private final BarcodePDFService pdfService;
@PostMapping("/generate")
public ResponseEntity<BarcodeResponse> generateBarcode(@Valid @RequestBody BarcodeRequest request) {
BarcodeResponse response = barcodeService.generateBarcode(request);
HttpStatus status = response.isSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST;
return ResponseEntity.status(status).body(response);
}
@PostMapping("/generate-to-file")
public ResponseEntity<BarcodeResponse> generateBarcodeToFile(@Valid @RequestBody BarcodeRequest request) {
BarcodeResponse response = barcodeService.generateBarcodeToFile(request);
HttpStatus status = response.isSuccess() ? HttpStatus.OK : HttpStatus.BAD_REQUEST;
return ResponseEntity.status(status).body(response);
}
@PostMapping("/generate-batch")
public ResponseEntity<BatchBarcodeResult> generateBatchBarcodes(@Valid @RequestBody List<BarcodeRequest> requests) {
if (requests.size() > 100) {
return ResponseEntity.badRequest().body(
BatchBarcodeResult.builder()
.message("Maximum 100 barcodes per batch allowed")
.build()
);
}
try {
BatchBarcodeResult result = batchBarcodeService.generateBatchBarcodes(requests).get();
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("Batch barcode generation failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(BatchBarcodeResult.builder()
.message("Batch generation failed: " + e.getMessage())
.build());
}
}
@PostMapping("/generate-pdf")
public ResponseEntity<byte[]> generateBarcodePDF(@Valid @RequestBody BarcodePDFRequest request) {
try {
byte[] pdfBytes = pdfService.generateBarcodePDF(request);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "attachment; filename=\"barcodes.pdf\"")
.body(pdfBytes);
} catch (Exception e) {
log.error("PDF generation failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/supported-types")
public ResponseEntity<List<BarcodeType>> getSupportedBarcodeTypes() {
List<BarcodeType> types = barcodeService.getSupportedBarcodeTypes();
return ResponseEntity.ok(types);
}
@GetMapping("/validate")
public ResponseEntity<ValidationResult> validateBarcode(
@RequestParam BarcodeType type,
@RequestParam String data) {
BarcodeValidationService validationService = new BarcodeValidationService();
ValidationResult result = validationService.validateBarcode(type, data);
return ResponseEntity.ok(result);
}
@GetMapping("/base64")
public ResponseEntity<Map<String, String>> generateBarcodeBase64(@Valid BarcodeRequest request) {
try {
String base64Image = barcodeService.generateBarcodeAsBase64(request);
Map<String, String> response = new HashMap<>();
response.put("barcodeType", request.getBarcodeType().name());
response.put("data", request.getData());
response.put("imageBase64", base64Image);
response.put("mimeType", "image/png");
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("Base64 barcode generation failed", e);
return ResponseEntity.badRequest().body(
Map.of("error", "Failed to generate barcode: " + e.getMessage()));
}
}
}
2. Barcode Download Controller
@RestController
@RequestMapping("/api/download")
@Slf4j
public class BarcodeDownloadController {
private final BarcodeService barcodeService;
@GetMapping("/barcode")
public ResponseEntity<byte[]> downloadBarcode(@Valid BarcodeRequest request) {
try {
byte[] barcodeData = barcodeService.generateBarcodeAsBytes(request);
String fileName = request.getFileName() != null ?
request.getFileName() :
"barcode." + (request.getImageFormat() != null ?
request.getImageFormat().name().toLowerCase() : "png");
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(getMimeType(request.getImageFormat())))
.header("Content-Disposition", "attachment; filename=\"" + fileName + "\"")
.body(barcodeData);
} catch (Exception e) {
log.error("Barcode download failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/barcode-sheet")
public ResponseEntity<byte[]> downloadBarcodeSheet(
@RequestParam List<String> data,
@RequestParam(defaultValue = "QR_CODE") BarcodeType barcodeType,
@RequestParam(defaultValue = "Barcode Sheet") String sheetName) {
try {
List<BarcodeRequest> barcodeRequests = data.stream()
.map(item -> BarcodeRequest.builder()
.data(item)
.barcodeType(barcodeType)
.includeText(true)
.build())
.collect(Collectors.toList());
BarcodePDFService pdfService = new BarcodePDFService(barcodeService);
byte[] pdfBytes = pdfService.generateBarcodeSheet(barcodeRequests, sheetName);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.header("Content-Disposition", "attachment; filename=\"barcode-sheet.pdf\"")
.body(pdfBytes);
} catch (Exception e) {
log.error("Barcode sheet download failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
private String getMimeType(ImageFormat format) {
if (format == null) return "image/png";
switch (format) {
case JPEG: return "image/jpeg";
case GIF: return "image/gif";
default: return "image/png";
}
}
}
Testing
1. Unit Tests
@ExtendWith(MockitoExtension.class)
class BarcodeServiceTest {
@Mock
private BarcodeConfig config;
@InjectMocks
private BarcodeServiceImpl barcodeService;
@Test
void whenGenerateQRCode_thenSuccess() {
// Given
BarcodeRequest request = BarcodeRequest.builder()
.data("https://example.com")
.barcodeType(BarcodeType.QR_CODE)
.imageFormat(ImageFormat.PNG)
.build();
when(config.getWidth()).thenReturn(300);
when(config.getHeight()).thenReturn(300);
when(config.getImageFormat()).thenReturn("PNG");
// When
BarcodeResponse response = barcodeService.generateBarcode(request);
// Then
assertTrue(response.isSuccess());
assertNotNull(response.getBase64Image());
assertNotNull(response.getImageData());
}
@Test
void whenInvalidEAN13_thenThrowException() {
// Given
BarcodeRequest request = BarcodeRequest.builder()
.data("123456789012") // Invalid length
.barcodeType(BarcodeType.EAN_13)
.build();
// When & Then
assertThrows(InvalidBarcodeDataException.class, () -> {
barcodeService.generateBarcode(request);
});
}
@Test
void whenValidateEAN13_thenReturnCorrectCheckDigit() {
// Given
String validEAN13 = "5901234123457"; // Valid EAN-13 with correct check digit
// When
boolean isValid = barcodeService.validateBarcodeData(BarcodeType.EAN_13, validEAN13);
// Then
assertTrue(isValid);
}
}
2. Integration Test
@SpringBootTest
@TestPropertySource(properties = {
"barcode.output-directory=./test-barcodes/",
"barcode.width=300",
"barcode.height=150"
})
class BarcodeIntegrationTest {
@Autowired
private BarcodeService barcodeService;
@Test
void testCompleteBarcodeGenerationFlow() {
// Given
BarcodeRequest request = BarcodeRequest.builder()
.data("TEST123")
.barcodeType(BarcodeType.CODE_128)
.imageFormat(ImageFormat.PNG)
.includeText(true)
.fileName("test-barcode")
.build();
// When
BarcodeResponse response = barcodeService.generateBarcodeToFile(request);
// Then
assertTrue(response.isSuccess());
assertNotNull(response.getFilePath());
File barcodeFile = new File(response.getFilePath());
assertTrue(barcodeFile.exists());
assertTrue(barcodeFile.length() > 0);
// Cleanup
barcodeFile.delete();
}
}
Best Practices
- Error Handling: Comprehensive exception handling for different failure scenarios
- Validation: Strict data validation for each barcode type
- Performance: Async processing for batch operations
- Memory Management: Proper resource cleanup with try-with-resources
- Flexibility: Support for multiple output formats and configurations
- Scalability: Batch processing and ZIP file generation for large volumes
- Standards Compliance: Proper implementation of barcode standards
This barcode generation system provides a complete solution for generating various barcode types in Java, suitable for integration into inventory systems, product labeling, ticketing systems, and any application requiring barcode functionality.