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.