Table of Contents
- Introduction to iText
- Project Setup and Dependencies
- Basic PDF Creation
- Text Styling and Formatting
- Tables and Layouts
- Images and Graphics
- Advanced Features
- PDF Forms and Interactive Elements
- Security and Encryption
- Performance Optimization
Introduction to iText
iText is a powerful Java library for creating and manipulating PDF documents. It provides comprehensive features for generating complex PDFs with text, images, tables, forms, and security features.
Key Features:
- Text manipulation: Advanced typography and formatting
- Table creation: Complex table layouts with styling
- Image support: Embed various image formats
- Forms: Interactive PDF forms
- Security: Encryption and digital signatures
- Accessibility: PDF/UA compliance
Project Setup and Dependencies
1. Maven Dependencies
<!-- pom.xml --> <properties> <itext.version>7.2.5</itext.version> </properties> <dependencies> <!-- iText 7 Core --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>${itext.version}</version> <type>pom</type> </dependency> <!-- PDF/A module --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>pdfa</artifactId> <version>${itext.version}</version> </dependency> <!-- Forms module --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>forms</artifactId> <version>${itext.version}</version> </dependency> <!-- Layout module (for advanced layouts) --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>layout</artifactId> <version>${itext.version}</version> </dependency> <!-- HTML to PDF conversion --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>html2pdf</artifactId> <version>${itext.version}</version> </dependency> <!-- Barcodes --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>barcodes</artifactId> <version>${itext.version}</version> </dependency> </dependencies> 2. Spring Boot Configuration
// PdfConfig.java @Configuration public class PdfConfig { @Bean public PdfFontService pdfFontService() { return new PdfFontService(); } @Bean public LicenseKeyLoader licenseKeyLoader() { // For commercial use - load license key return new LicenseKeyLoader(); } } // LicenseKeyLoader.java @Component public class LicenseKeyLoader { private static final Logger logger = LoggerFactory.getLogger(LicenseKeyLoader.class); @PostConstruct public void loadLicense() { try { // For commercial use, load your license file // LicenseKey.loadLicenseFile("path/to/itextkey.xml"); logger.info("iText license loaded successfully"); } catch (Exception e) { logger.warn("iText license not loaded - using AGPL version"); } } } Basic PDF Creation
1. Simple PDF Generation Service
// BasicPdfService.java @Service public class BasicPdfService { private static final Logger logger = LoggerFactory.getLogger(BasicPdfService.class); public byte[] createSimplePdf(String title, String content) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Add title Paragraph titleParagraph = new Paragraph(title) .setFontSize(20) .setBold() .setTextAlignment(TextAlignment.CENTER); document.add(titleParagraph); // Add content Paragraph contentParagraph = new Paragraph(content) .setFontSize(12) .setTextAlignment(TextAlignment.JUSTIFIED); document.add(contentParagraph); document.close(); logger.info("Simple PDF created successfully"); return baos.toByteArray(); } catch (Exception e) { logger.error("Error creating PDF", e); throw new IOException("Failed to create PDF", e); } } public byte[] createMultiPagePdf(List<String> pages) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); for (int i = 0; i < pages.size(); i++) { // Add page content Paragraph pageParagraph = new Paragraph("Page " + (i + 1) + "\n" + pages.get(i)) .setFontSize(12); document.add(pageParagraph); // Add page break if not last page if (i < pages.size() - 1) { document.add(new AreaBreak(AreaBreakType.NEXT_PAGE)); } } document.close(); return baos.toByteArray(); } } public void createPdfToFile(String filePath, String content) throws IOException { PdfWriter writer = new PdfWriter(filePath); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); try { document.add(new Paragraph(content)); logger.info("PDF saved to: {}", filePath); } finally { document.close(); } } } 2. PDF Generation Controller
// PdfController.java @RestController @RequestMapping("/api/pdf") public class PdfController { private final BasicPdfService basicPdfService; private final AdvancedPdfService advancedPdfService; public PdfController(BasicPdfService basicPdfService, AdvancedPdfService advancedPdfService) { this.basicPdfService = basicPdfService; this.advancedPdfService = advancedPdfService; } @PostMapping("/simple") public ResponseEntity<byte[]> generateSimplePdf(@RequestBody SimplePdfRequest request) { try { byte[] pdfBytes = basicPdfService.createSimplePdf(request.getTitle(), request.getContent()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"document.pdf\"") .contentType(MediaType.APPLICATION_PDF) .body(pdfBytes); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } @GetMapping("/download/{documentId}") public ResponseEntity<byte[]> downloadDocument(@PathVariable String documentId) { try { // In real application, fetch document data from database String title = "Document " + documentId; String content = "This is the content for document " + documentId; byte[] pdfBytes = basicPdfService.createSimplePdf(title, content); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"document_" + documentId + ".pdf\"") .contentType(MediaType.APPLICATION_PDF) .body(pdfBytes); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); } } } // SimplePdfRequest.java public class SimplePdfRequest { private String title; private String content; // Constructors, getters, and setters public SimplePdfRequest() {} public SimplePdfRequest(String title, String content) { this.title = title; this.content = content; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } } Text Styling and Formatting
1. Font Service and Management
// PdfFontService.java @Service public class PdfFontService { private final Map<String, PdfFont> fontCache = new ConcurrentHashMap<>(); public PdfFont getDefaultFont() throws IOException { return getFont(PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); } public PdfFont getFont(PdfFontFactory.EmbeddingStrategy strategy) throws IOException { String cacheKey = "default-" + strategy.name(); return fontCache.computeIfAbsent(cacheKey, key -> { try { return PdfFontFactory.createFont(StandardFonts.HELVETICA, strategy); } catch (IOException e) { throw new RuntimeException("Failed to create font", e); } }); } public PdfFont getBoldFont() throws IOException { return getFont("bold", StandardFonts.HELVETICA_BOLD); } public PdfFont getItalicFont() throws IOException { return getFont("italic", StandardFonts.HELVETICA_OBLIQUE); } public PdfFont getFont(String name, String fontName) throws IOException { return fontCache.computeIfAbsent(name, key -> { try { return PdfFontFactory.createFont(fontName, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); } catch (IOException e) { throw new RuntimeException("Failed to create font: " + fontName, e); } }); } public PdfFont loadCustomFont(String fontPath) throws IOException { return fontCache.computeIfAbsent(fontPath, key -> { try { return PdfFontFactory.createFont(fontPath, PdfFontFactory.EmbeddingStrategy.FORCE_EMBEDDED); } catch (IOException e) { throw new RuntimeException("Failed to load custom font: " + fontPath, e); } }); } } 2. Advanced Text Styling Service
// TextStylingService.java @Service public class TextStylingService { private final PdfFontService fontService; public TextStylingService(PdfFontService fontService) { this.fontService = fontService; } public Paragraph createTitle(String text) throws IOException { return new Paragraph(text) .setFont(fontService.getBoldFont()) .setFontSize(24) .setFontColor(ColorConstants.BLUE) .setTextAlignment(TextAlignment.CENTER) .setMarginBottom(20); } public Paragraph createHeading(String text, int level) throws IOException { float fontSize = switch (level) { case 1 -> 18; case 2 -> 16; case 3 -> 14; default -> 12; }; return new Paragraph(text) .setFont(fontService.getBoldFont()) .setFontSize(fontSize) .setFontColor(ColorConstants.DARK_GRAY) .setMarginBottom(10) .setMarginTop(15); } public Paragraph createBodyText(String text) throws IOException { return new Paragraph(text) .setFont(fontService.getDefaultFont()) .setFontSize(12) .setFontColor(ColorConstants.BLACK) .setTextAlignment(TextAlignment.JUSTIFIED) .setMarginBottom(8); } public Paragraph createHighlightedText(String text) throws IOException { return new Paragraph(text) .setFont(fontService.getBoldFont()) .setFontSize(12) .setFontColor(ColorConstants.RED) .setBackgroundColor(ColorConstants.YELLOW) .setPadding(5); } public Paragraph createQuote(String text, String author) throws IOException { Div quoteDiv = new Div() .setBackgroundColor(ColorConstants.LIGHT_GRAY) .setPadding(15) .setMargin(10); Paragraph quoteText = new Paragraph("\"" + text + "\"") .setFont(fontService.getItalicFont()) .setFontSize(12) .setFontColor(ColorConstants.DARK_GRAY); Paragraph authorText = new Paragraph("- " + author) .setFont(fontService.getDefaultFont()) .setFontSize(10) .setFontColor(ColorConstants.GRAY) .setTextAlignment(TextAlignment.RIGHT); quoteDiv.add(quoteText); quoteDiv.add(authorText); // Since we can't return Div as Paragraph, create a container paragraph return new Paragraph().add(quoteDiv); } public List createBulletedList(List<String> items) throws IOException { List list = new List() .setSymbolIndent(12) .setListSymbol("\u2022") // Bullet character .setFont(fontService.getDefaultFont()); for (String item : items) { list.add(new ListItem(item)); } return list; } public List createNumberedList(List<String> items) throws IOException { List list = new List() .setListSymbol(ListNumberingType.DECIMAL) .setFont(fontService.getDefaultFont()); for (String item : items) { list.add(new ListItem(item)); } return list; } } 3. Styled Document Service
// StyledPdfService.java @Service public class StyledPdfService { private final TextStylingService stylingService; private final PdfFontService fontService; public StyledPdfService(TextStylingService stylingService, PdfFontService fontService) { this.stylingService = stylingService; this.fontService = fontService; } public byte[] createStyledDocument(StyledDocumentRequest request) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Add title document.add(stylingService.createTitle(request.getTitle())); // Add author and date Paragraph metaInfo = new Paragraph() .setFontSize(10) .setFontColor(ColorConstants.GRAY) .setTextAlignment(TextAlignment.CENTER) .add("Author: " + request.getAuthor() + " | ") .add("Date: " + LocalDate.now().toString()); document.add(metaInfo); document.add(new Paragraph().setHeight(20)); // Spacing // Add content sections for (DocumentSection section : request.getSections()) { document.add(stylingService.createHeading(section.getHeading(), section.getLevel())); for (String paragraph : section.getParagraphs()) { document.add(stylingService.createBodyText(paragraph)); } // Add lists if present if (!section.getBulletPoints().isEmpty()) { document.add(stylingService.createBulletedList(section.getBulletPoints())); } document.add(new Paragraph().setHeight(10)); // Section spacing } // Add footer Paragraph footer = new Paragraph("Generated by PDF Service") .setFontSize(8) .setFontColor(ColorConstants.LIGHT_GRAY) .setTextAlignment(TextAlignment.CENTER) .setMarginTop(20); document.add(footer); document.close(); return baos.toByteArray(); } } public byte[] createInvoice(InvoiceData invoice) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Invoice header document.add(stylingService.createTitle("INVOICE")); // Company and client info in two columns float[] columnWidths = {1, 1}; Table headerTable = new Table(columnWidths); // From column Cell fromCell = new Cell() .add(stylingService.createHeading("From:", 3)) .add(new Paragraph(invoice.getFromCompany())) .add(new Paragraph(invoice.getFromAddress())) .add(new Paragraph(invoice.getFromEmail())) .add(new Paragraph(invoice.getFromPhone())); // To column Cell toCell = new Cell() .add(stylingService.createHeading("To:", 3)) .add(new Paragraph(invoice.getToCompany())) .add(new Paragraph(invoice.getToAddress())) .add(new Paragraph(invoice.getToEmail())); headerTable.addCell(fromCell); headerTable.addCell(toCell); document.add(headerTable); // Invoice details document.add(new Paragraph().setHeight(15)); float[] detailWidths = {1, 1, 1, 1}; Table detailTable = new Table(detailWidths); // Table headers detailTable.addHeaderCell(createHeaderCell("Invoice No")); detailTable.addHeaderCell(createHeaderCell("Date")); detailTable.addHeaderCell(createHeaderCell("Due Date")); detailTable.addHeaderCell(createHeaderCell("Total")); // Table data detailTable.addCell(createDataCell(invoice.getInvoiceNumber())); detailTable.addCell(createDataCell(invoice.getInvoiceDate().toString())); detailTable.addCell(createDataCell(invoice.getDueDate().toString())); detailTable.addCell(createDataCell("$" + invoice.getTotalAmount())); document.add(detailTable); document.close(); return baos.toByteArray(); } } private Cell createHeaderCell(String text) throws IOException { return new Cell() .add(new Paragraph(text) .setFont(fontService.getBoldFont()) .setFontSize(10)) .setBackgroundColor(ColorConstants.LIGHT_GRAY) .setPadding(5); } private Cell createDataCell(String text) throws IOException { return new Cell() .add(new Paragraph(text) .setFont(fontService.getDefaultFont()) .setFontSize(10)) .setPadding(5); } } // StyledDocumentRequest.java public class StyledDocumentRequest { private String title; private String author; private List<DocumentSection> sections; // Constructors, getters, and setters public StyledDocumentRequest() {} public StyledDocumentRequest(String title, String author, List<DocumentSection> sections) { this.title = title; this.author = author; this.sections = sections; } // Getters and setters... } // DocumentSection.java public class DocumentSection { private String heading; private int level = 2; private List<String> paragraphs; private List<String> bulletPoints; // Constructors, getters, and setters... } // InvoiceData.java public class InvoiceData { private String invoiceNumber; private LocalDate invoiceDate; private LocalDate dueDate; private String fromCompany; private String fromAddress; private String fromEmail; private String fromPhone; private String toCompany; private String toAddress; private String toEmail; private BigDecimal totalAmount; private List<InvoiceItem> items; // Constructors, getters, and setters... } // InvoiceItem.java public class InvoiceItem { private String description; private int quantity; private BigDecimal unitPrice; private BigDecimal total; // Constructors, getters, and setters... } Tables and Layouts
1. Advanced Table Service
// TablePdfService.java @Service public class TablePdfService { private final PdfFontService fontService; public TablePdfService(PdfFontService fontService) { this.fontService = fontService; } public byte[] createDataTable(List<TableColumn> columns, List<Map<String, Object>> data) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Create table with dynamic column widths float[] columnWidths = new float[columns.size()]; Arrays.fill(columnWidths, 1); // Equal widths by default Table table = new Table(columnWidths); // Add header row for (TableColumn column : columns) { Cell headerCell = new Cell() .add(new Paragraph(column.getHeader()) .setFont(fontService.getBoldFont()) .setFontSize(10)) .setBackgroundColor(ColorConstants.LIGHT_GRAY) .setTextAlignment(column.getAlignment()) .setPadding(5); table.addHeaderCell(headerCell); } // Add data rows for (Map<String, Object> row : data) { for (TableColumn column : columns) { Object value = row.get(column.getKey()); String text = value != null ? value.toString() : ""; Cell dataCell = new Cell() .add(new Paragraph(text) .setFont(fontService.getDefaultFont()) .setFontSize(9)) .setTextAlignment(column.getAlignment()) .setPadding(3); // Apply conditional formatting if (column.getConditionalColor() != null) { Color color = column.getConditionalColor().apply(value); if (color != null) { dataCell.setBackgroundColor(color); } } table.addCell(dataCell); } } document.add(table); document.close(); return baos.toByteArray(); } } public byte[] createComplexReport(ReportData report) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Report title document.add(new Paragraph(report.getTitle()) .setFontSize(18) .setBold() .setTextAlignment(TextAlignment.CENTER)); // Summary section if (report.getSummary() != null) { document.add(new Paragraph("Summary") .setFontSize(14) .setBold() .setMarginTop(15)); document.add(new Paragraph(report.getSummary()) .setFontSize(10) .setMarginBottom(10)); } // Data tables for (ReportTable tableData : report.getTables()) { document.add(new Paragraph(tableData.getTitle()) .setFontSize(12) .setBold() .setMarginTop(10)); Table table = createReportTable(tableData); document.add(table); } document.close(); return baos.toByteArray(); } } private Table createReportTable(ReportTable tableData) throws IOException { float[] columnWidths = tableData.getColumnWidths(); Table table = new Table(columnWidths); // Header for (String header : tableData.getHeaders()) { table.addHeaderCell(createStyledCell(header, true, TextAlignment.CENTER)); } // Data for (List<String> row : tableData.getRows()) { for (int i = 0; i < row.size(); i++) { TextAlignment alignment = i == 0 ? TextAlignment.LEFT : TextAlignment.RIGHT; table.addCell(createStyledCell(row.get(i), false, alignment)); } } // Footer if exists if (tableData.getFooter() != null && !tableData.getFooter().isEmpty()) { for (String footer : tableData.getFooter()) { Cell footerCell = createStyledCell(footer, true, TextAlignment.RIGHT); footerCell.setColspan(tableData.getHeaders().size()); table.addCell(footerCell); } } return table; } private Cell createStyledCell(String text, boolean isHeader, TextAlignment alignment) throws IOException { Paragraph paragraph = new Paragraph(text) .setFont(isHeader ? fontService.getBoldFont() : fontService.getDefaultFont()) .setFontSize(isHeader ? 10 : 9); Cell cell = new Cell().add(paragraph).setTextAlignment(alignment).setPadding(4); if (isHeader) { cell.setBackgroundColor(ColorConstants.LIGHT_GRAY); } return cell; } public byte[] createMultiColumnLayout(List<ColumnContent> columns) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Create multi-column layout float[] columnWidths = new float[columns.size()]; for (int i = 0; i < columns.size(); i++) { columnWidths[i] = columns.get(i).getWidth(); } Table layoutTable = new Table(columnWidths); for (ColumnContent column : columns) { Cell columnCell = new Cell() .setPadding(10) .setBorder(Border.NO_BORDER); // Add column title if (column.getTitle() != null) { columnCell.add(new Paragraph(column.getTitle()) .setFont(fontService.getBoldFont()) .setFontSize(12) .setMarginBottom(5)); } // Add column content for (String content : column.getContent()) { columnCell.add(new Paragraph(content) .setFont(fontService.getDefaultFont()) .setFontSize(10) .setMarginBottom(3)); } layoutTable.addCell(columnCell); } document.add(layoutTable); document.close(); return baos.toByteArray(); } } } // TableColumn.java public class TableColumn { private String key; private String header; private TextAlignment alignment = TextAlignment.LEFT; private Function<Object, Color> conditionalColor; // Constructors, getters, and setters... public TableColumn() {} public TableColumn(String key, String header) { this.key = key; this.header = header; } public TableColumn(String key, String header, TextAlignment alignment) { this.key = key; this.header = header; this.alignment = alignment; } // Getters and setters... } // ReportData.java public class ReportData { private String title; private String summary; private List<ReportTable> tables; // Constructors, getters, and setters... } // ReportTable.java public class ReportTable { private String title; private List<String> headers; private List<List<String>> rows; private List<String> footer; private float[] columnWidths; // Constructors, getters, and setters... } // ColumnContent.java public class ColumnContent { private String title; private List<String> content; private float width = 1f; // Relative width // Constructors, getters, and setters... } Images and Graphics
1. Image Handling Service
// ImagePdfService.java @Service public class ImagePdfService { private static final Logger logger = LoggerFactory.getLogger(ImagePdfService.class); public byte[] createPdfWithImages(List<ImageData> images) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); for (ImageData imageData : images) { try { Image image = createImage(imageData); // Add caption if provided if (imageData.getCaption() != null) { Paragraph caption = new Paragraph(imageData.getCaption()) .setFontSize(10) .setItalic() .setTextAlignment(TextAlignment.CENTER); document.add(caption); } document.add(image); document.add(new Paragraph().setHeight(10)); // Spacing } catch (Exception e) { logger.warn("Failed to add image: {}", imageData.getSource(), e); // Continue with next image } } document.close(); return baos.toByteArray(); } } public byte[] createImageGallery(List<ImageData> images, int columns) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Calculate column widths float[] columnWidths = new float[columns]; Arrays.fill(columnWidths, 1f); Table galleryTable = new Table(columnWidths); galleryTable.setWidth(UnitValue.createPercentValue(100)); for (int i = 0; i < images.size(); i++) { ImageData imageData = images.get(i); try { Image image = createImage(imageData); image.setAutoScale(true); image.setHorizontalAlignment(HorizontalAlignment.CENTER); Cell cell = new Cell() .add(image) .setPadding(5) .setBorder(Border.NO_BORDER); if (imageData.getCaption() != null) { Paragraph caption = new Paragraph(imageData.getCaption()) .setFontSize(8) .setTextAlignment(TextAlignment.CENTER); cell.add(caption); } galleryTable.addCell(cell); } catch (Exception e) { logger.warn("Failed to add image to gallery: {}", imageData.getSource(), e); // Add empty cell to maintain layout galleryTable.addCell(new Cell().setBorder(Border.NO_BORDER)); } } // Fill remaining cells if needed int remainingCells = columns - (images.size() % columns); if (remainingCells < columns) { for (int i = 0; i < remainingCells; i++) { galleryTable.addCell(new Cell().setBorder(Border.NO_BORDER)); } } document.add(galleryTable); document.close(); return baos.toByteArray(); } } private Image createImage(ImageData imageData) throws IOException { Image image; if (imageData.getImageBytes() != null) { ImageDataFactory imageFactory = ImageDataFactory.create(imageData.getImageBytes()); image = new Image(imageFactory); } else if (imageData.getSource().startsWith("http")) { // Download image from URL byte[] imageBytes = downloadImage(imageData.getSource()); ImageDataFactory imageFactory = ImageDataFactory.create(imageBytes); image = new Image(imageFactory); } else { // Load from file system ImageDataFactory imageFactory = ImageDataFactory.create(imageData.getSource()); image = new Image(imageFactory); } // Apply scaling if (imageData.getMaxWidth() > 0 || imageData.getMaxHeight() > 0) { image.scaleToFit(imageData.getMaxWidth(), imageData.getMaxHeight()); } if (imageData.getWidth() > 0 && imageData.getHeight() > 0) { image.setFixedPosition(imageData.getX(), imageData.getY()); image.setFixedDimension(imageData.getWidth(), imageData.getHeight()); } return image; } private byte[] downloadImage(String imageUrl) throws IOException { try { URL url = new URL(imageUrl); try (InputStream in = url.openStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { baos.write(buffer, 0, bytesRead); } return baos.toByteArray(); } } catch (Exception e) { throw new IOException("Failed to download image from: " + imageUrl, e); } } public byte[] createPdfWithWatermark(String content, String watermarkText) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); // Add watermark handler pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, new WatermarkEventHandler(watermarkText)); Document document = new Document(pdfDoc); document.add(new Paragraph(content)); document.close(); return baos.toByteArray(); } } private static class WatermarkEventHandler implements IEventHandler { private final String watermarkText; public WatermarkEventHandler(String watermarkText) { this.watermarkText = watermarkText; } @Override public void handleEvent(Event event) { PdfDocumentEvent docEvent = (PdfDocumentEvent) event; PdfPage page = docEvent.getPage(); PdfCanvas pdfCanvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), docEvent.getDocument()); Canvas canvas = new Canvas(pdfCanvas, page.getPageSize()); canvas.setFontColor(ColorConstants.LIGHT_GRAY); canvas.setFontSize(60); canvas.showTextAligned(watermarkText, page.getPageSize().getWidth() / 2, page.getPageSize().getHeight() / 2, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45); // Rotate 45 degrees } } } // ImageData.java public class ImageData { private String source; private byte[] imageBytes; private String caption; private float maxWidth; private float maxHeight; private float width; private float height; private float x; private float y; // Constructors, getters, and setters... public ImageData() {} public ImageData(String source) { this.source = source; } public ImageData(byte[] imageBytes) { this.imageBytes = imageBytes; } // Getters and setters... } Advanced Features
1. PDF/A Compliance
// PdfAComplianceService.java @Service public class PdfAComplianceService { public byte[] createPdfADocument(String title, String content) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); // Create PDF/A compliant document PdfADocument pdfDoc = new PdfADocument(writer, PdfAConformanceLevel.PDF_A_3B, new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", Files.readAllBytes(Paths.get("sRGB_CS_profile.icm")))); Document document = new Document(pdfDoc); // Add required metadata PdfDocumentInfo info = pdfDoc.getDocumentInfo(); info.setTitle(title); info.setAuthor("PDF Service"); info.setSubject("PDF/A Compliant Document"); info.setKeywords("PDF/A, compliance"); info.setCreator("iText PDF Service"); // Add content document.add(new Paragraph(title).setFontSize(18).setBold()); document.add(new Paragraph(content).setFontSize(12)); document.close(); return baos.toByteArray(); } } } 2. Barcode Generation
// BarcodeService.java @Service public class BarcodeService { public byte[] createPdfWithBarcode(String content, String barcodeData, BarcodeType type) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Add content document.add(new Paragraph(content)); document.add(new Paragraph().setHeight(20)); // Add barcode document.add(new Paragraph("Barcode (" + type + "):")); Barcode barcode = createBarcode(barcodeData, type, pdfDoc); Image barcodeImage = new Image(barcode.createFormXObject(null, pdfDoc)); document.add(barcodeImage); document.close(); return baos.toByteArray(); } } private Barcode createBarcode(String data, BarcodeType type, PdfDocument pdfDoc) { return switch (type) { case CODE128 -> { Barcode128 barcode = new Barcode128(pdfDoc); barcode.setCode(data); barcode.setCodeType(Barcode128.CODE128); yield barcode; } case QR -> { BarcodeQRCode barcode = new BarcodeQRCode(data); yield barcode; } case PDF417 -> { BarcodePDF417 barcode = new BarcodePDF417(); barcode.setCode(data); yield barcode; } default -> throw new IllegalArgumentException("Unsupported barcode type: " + type); }; } public enum BarcodeType { CODE128, QR, PDF417 } } PDF Forms and Interactive Elements
1. Interactive Form Service
// FormPdfService.java @Service public class FormPdfService { public byte[] createFillableForm(FormTemplate template) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); // Add form title document.add(new Paragraph(template.getTitle()) .setFontSize(16) .setBold() .setTextAlignment(TextAlignment.CENTER)); // Create form PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true); // Add form fields float yPosition = pdfDoc.getDefaultPageSize().getTop() - 100; for (FormField field : template.getFields()) { yPosition = addFormField(document, form, field, yPosition); yPosition -= 30; // Spacing between fields } document.close(); return baos.toByteArray(); } } private float addFormField(Document document, PdfAcroForm form, FormField field, float yPosition) { PdfPage page = document.getPdfDocument().getLastPage(); Rectangle rect = new Rectangle(50, yPosition - 20, 200, 20); PdfFormField formField; switch (field.getType()) { case TEXT: formField = PdfTextFormField.createText(document.getPdfDocument(), rect, field.getName()); if (field.getDefaultValue() != null) { formField.setValue(field.getDefaultValue()); } break; case CHECKBOX: formField = PdfFormField.createCheckBox(document.getPdfDocument(), rect, field.getName(), "Yes"); break; case COMBOBOX: formField = PdfFormField.createComboBox(document.getPdfDocument(), rect, field.getName(), field.getDefaultValue()); if (field.getOptions() != null) { for (String option : field.getOptions()) { formField.addOption(option); } } break; default: throw new IllegalArgumentException("Unsupported field type: " + field.getType()); } // Set field properties formField.setRequired(field.isRequired()); formField.setReadOnly(field.isReadOnly()); // Add field label try { document.add(new Paragraph(field.getLabel()) .setFixedPosition(50, yPosition, 200) .setFontSize(10)); } catch (IOException e) { throw new RuntimeException("Failed to add field label", e); } form.addField(formField); return yPosition; } public byte[] fillExistingForm(byte[] templatePdf, Map<String, String> formData) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfReader reader = new PdfReader(new ByteArrayInputStream(templatePdf)); PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(reader, writer)) { PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, false); if (form != null) { for (Map.Entry<String, String> entry : formData.entrySet()) { PdfFormField field = form.getField(entry.getKey()); if (field != null) { field.setValue(entry.getValue()); } } form.flattenFields(); // Make form non-editable } pdfDoc.close(); return baos.toByteArray(); } } } // FormTemplate.java public class FormTemplate { private String title; private List<FormField> fields; // Constructors, getters, and setters... } // FormField.java public class FormField { private String name; private String label; private FieldType type; private String defaultValue; private List<String> options; private boolean required; private boolean readOnly; // Constructors, getters, and setters... } public enum FieldType { TEXT, CHECKBOX, COMBOBOX, RADIO, LIST } Security and Encryption
1. PDF Security Service
// PdfSecurityService.java @Service public class PdfSecurityService { public byte[] encryptPdf(byte[] pdfBytes, String userPassword, String ownerPassword, EncryptionProperties properties) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfReader reader = new PdfReader(new ByteArrayInputStream(pdfBytes)); PdfWriter writer = new PdfWriter(baos, new WriterProperties() .setStandardEncryption( userPassword.getBytes(), ownerPassword.getBytes(), properties.getPermissions(), properties.getEncryptionAlgorithm()))) { PdfDocument pdfDoc = new PdfDocument(reader, writer); pdfDoc.close(); return baos.toByteArray(); } } public byte[] addDigitalSignature(byte[] pdfBytes, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider, String reason, String location) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfReader reader = new PdfReader(new ByteArrayInputStream(pdfBytes)); PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(reader, writer)) { // Create signature appearance Rectangle rect = new Rectangle(36, 648, 200, 100); PdfSignatureAppearance appearance = new PdfSignatureAppearance(pdfDoc, rect); appearance.setReason(reason); appearance.setLocation(location); appearance.setPageNumber(1); // Create signature IExternalSignature signature = new PrivateKeySignature(pk, digestAlgorithm, provider); IExternalDigest digest = new BouncyCastleDigest(); PdfSigner.signDetached(appearance, digest, signature, chain, null, null, null, 0, PdfSigner.CryptoStandard.CMS); pdfDoc.close(); return baos.toByteArray(); } } public byte[] addWatermark(byte[] pdfBytes, String watermarkText) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PdfReader reader = new PdfReader(new ByteArrayInputStream(pdfBytes)); PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(reader, writer)) { int pageCount = pdfDoc.getNumberOfPages(); for (int i = 1; i <= pageCount; i++) { PdfPage page = pdfDoc.getPage(i); PdfCanvas over = new PdfCanvas(page.newContentStreamAfter(), page.getResources(), pdfDoc); Canvas canvas = new Canvas(over, page.getPageSize()); canvas.setFontColor(ColorConstants.LIGHT_GRAY); canvas.setFontSize(48); canvas.setOpacity(0.3f); canvas.showTextAligned(watermarkText, page.getPageSize().getWidth() / 2, page.getPageSize().getHeight() / 2, TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45); canvas.close(); } pdfDoc.close(); return baos.toByteArray(); } } } // EncryptionProperties.java public class EncryptionProperties { private int permissions = EncryptionConstants.ALLOW_SCREENREADERS; private int encryptionAlgorithm = EncryptionConstants.STANDARD_ENCRYPTION_128; // Constructors, getters, and setters... public EncryptionProperties() {} public EncryptionProperties(int permissions, int encryptionAlgorithm) { this.permissions = permissions; this.encryptionAlgorithm = encryptionAlgorithm; } // Getters and setters... } Performance Optimization
1. PDF Generation with Caching
// OptimizedPdfService.java @Service public class OptimizedPdfService { private final Cache<String, byte[]> pdfCache; private final BasicPdfService basicPdfService; public OptimizedPdfService(BasicPdfService basicPdfService) { this.basicPdfService = basicPdfService; // Configure cache this.pdfCache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(1, TimeUnit.HOURS) .build(); } public byte[] generateCachedPdf(String cacheKey, String title, String content) throws IOException { return pdfCache.get(cacheKey, key -> { try { return basicPdfService.createSimplePdf(title, content); } catch (IOException e) { throw new RuntimeException("Failed to generate PDF", e); } }); } public void invalidateCache(String cacheKey) { pdfCache.invalidate(cacheKey); } public void clearCache() { pdfCache.invalidateAll(); } } // BatchPdfService.java @Service public class BatchPdfService { private final ExecutorService executorService; public BatchPdfService() { this.executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); } public List<byte[]> generatePdfBatch(List<PdfGenerationTask> tasks) { List<CompletableFuture<byte[]>> futures = tasks.stream() .map(task -> CompletableFuture.supplyAsync(() -> { try { return generatePdf(task); } catch (Exception e) { throw new CompletionException(e); } }, executorService)) .collect(Collectors.toList()); return futures.stream() .map(CompletableFuture::join) .collect(Collectors.toList()); } private byte[] generatePdf(PdfGenerationTask task) throws IOException { // Implementation depends on task type // This is a simplified example try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); document.add(new Paragraph(task.getContent())); document.close(); return baos.toByteArray(); } } @PreDestroy public void cleanup() { executorService.shutdown(); } } // PdfGenerationTask.java public class PdfGenerationTask { private String id; private String content; private PdfTemplate template; // Constructors, getters, and setters... } 2. Memory Management
// MemoryOptimizedPdfService.java @Service public class MemoryOptimizedPdfService { private static final Logger logger = LoggerFactory.getLogger(MemoryOptimizedPdfService.class); public void createLargePdfInChunks(String outputPath, List<DocumentChunk> chunks) throws IOException { PdfWriter writer = new PdfWriter(outputPath); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); try { for (DocumentChunk chunk : chunks) { processChunk(document, chunk); // Force garbage collection periodically for large documents if (chunk.getChunkNumber() % 10 == 0) { System.gc(); } } } finally { document.close(); } } private void processChunk(Document document, DocumentChunk chunk) throws IOException { switch (chunk.getType()) { case TEXT: document.add(new Paragraph(chunk.getContent())); break; case TABLE: // Add table processing break; case IMAGE: // Add image processing break; default: logger.warn("Unknown chunk type: {}", chunk.getType()); } } public byte[] createPdfWithMemoryLimit(String content, long maxMemoryBytes) throws IOException { MemoryLimitsAwareHandler memoryHandler = new MemoryLimitsAwareHandler(maxMemoryBytes); try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { PdfWriter writer = new PdfWriter(baos); writer.setMemoryLimitsAwareHandler(memoryHandler); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); document.add(new Paragraph(content)); document.close(); return baos.toByteArray(); } } } // DocumentChunk.java public class DocumentChunk { private int chunkNumber; private ChunkType type; private String content; // Constructors, getters, and setters... } enum ChunkType { TEXT, TABLE, IMAGE, HEADER, FOOTER } Conclusion
iText provides a comprehensive solution for PDF generation in Java applications. Key takeaways:
- Choose the right iText version (AGPL for open source, commercial for proprietary software)
- Use proper font management for consistent typography
- Implement caching for frequently generated documents
- Handle memory carefully for large PDF generation
- Use appropriate security features for sensitive documents
- Consider PDF/A compliance for long-term archiving
- Implement proper error handling and logging
- Optimize performance with batch processing and threading
By following these patterns and best practices, you can build robust, efficient PDF generation services that meet your application's requirements.