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.