PDF Generation with iText in Java

Table of Contents

  1. Introduction to iText
  2. Project Setup and Dependencies
  3. Basic PDF Creation
  4. Text Styling and Formatting
  5. Tables and Layouts
  6. Images and Graphics
  7. Advanced Features
  8. PDF Forms and Interactive Elements
  9. Security and Encryption
  10. 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:

  1. Choose the right iText version (AGPL for open source, commercial for proprietary software)
  2. Use proper font management for consistent typography
  3. Implement caching for frequently generated documents
  4. Handle memory carefully for large PDF generation
  5. Use appropriate security features for sensitive documents
  6. Consider PDF/A compliance for long-term archiving
  7. Implement proper error handling and logging
  8. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper