- Published on
Generate High-Quality PDFs with Playwright Custom Variables
Creating high-quality PDFs with dynamic content is essential for modern business applications—from personalized reports to branded invoices and complex documentation.
In this advanced guide, you'll learn how to generate professional PDFs using Playwright Java with custom variables, enabling you to create dynamic, branded documents with consistent styling and personalized content. We'll also explore how LetMePDF.com can simplify this process for production environments.
🎯 Why Custom Variables Matter
Custom variables in PDF generation allow you to:
- Personalize content with user-specific data
- Maintain brand consistency across all documents
- Scale efficiently with reusable templates
- Reduce code duplication through parameterized generation
🛠️ Advanced PDF Generation Setup
1. Enhanced Playwright Configuration
Start with a more robust Playwright setup for high-quality output:
import com.microsoft.playwright.*;
import java.nio.file.Paths;
import java.util.Map;
import java.util.HashMap;
public class AdvancedPdfGenerator {
private final Playwright playwright;
private final Browser browser;
public AdvancedPdfGenerator() {
this.playwright = Playwright.create();
this.browser = playwright.chromium().launch(new BrowserType.LaunchOptions()
.setHeadless(true)
.setArgs("--no-sandbox", "--disable-dev-shm-usage"));
}
public void generatePdf(Map<String, Object> variables, String outputPath) {
try (BrowserContext context = browser.newContext()) {
Page page = context.newPage();
// Set viewport for consistent rendering
page.setViewportSize(1200, 800);
String htmlContent = buildHtmlWithVariables(variables);
page.setContent(htmlContent);
// High-quality PDF options
page.pdf(new Page.PdfOptions()
.setPath(Paths.get(outputPath))
.setFormat("A4")
.setPrintBackground(true)
.setMargin(new Page.PdfOptions.Margin()
.setTop("1in")
.setBottom("1in")
.setLeft("1in")
.setRight("1in"))
.setPreferCssPageSize(true));
}
}
private String buildHtmlWithVariables(Map<String, Object> variables) {
return String.format("""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
@page {
size: A4;
margin: 1in;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 20px;
}
.header {
text-align: center;
border-bottom: 2px solid %s;
padding-bottom: 20px;
margin-bottom: 30px;
}
.company-logo {
max-width: 200px;
height: auto;
}
.invoice-details {
display: flex;
justify-content: space-between;
margin-bottom: 30px;
}
.customer-info {
background-color: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.items-table {
width: 100%%;
border-collapse: collapse;
margin: 20px 0;
}
.items-table th,
.items-table td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
.items-table th {
background-color: %s;
color: white;
font-weight: bold;
}
.total-section {
text-align: right;
margin-top: 30px;
font-size: 18px;
font-weight: bold;
}
.footer {
margin-top: 50px;
text-align: center;
font-size: 12px;
color: #666;
border-top: 1px solid #ddd;
padding-top: 20px;
}
</style>
</head>
<body>
<div class="header">
<h1>%s</h1>
<p>%s</p>
</div>
<div class="invoice-details">
<div>
<h3>Invoice Details</h3>
<p><strong>Invoice #:</strong> %s</p>
<p><strong>Date:</strong> %s</p>
<p><strong>Due Date:</strong> %s</p>
</div>
<div>
<h3>Company Info</h3>
<p>%s</p>
<p>%s</p>
<p>%s</p>
</div>
</div>
<div class="customer-info">
<h3>Bill To:</h3>
<p><strong>%s</strong></p>
<p>%s</p>
<p>%s</p>
</div>
<table class="items-table">
<thead>
<tr>
<th>Description</th>
<th>Quantity</th>
<th>Unit Price</th>
<th>Total</th>
</tr>
</thead>
<tbody>
%s
</tbody>
</table>
<div class="total-section">
<p>Subtotal: $%.2f</p>
<p>Tax (%.1f%%): $%.2f</p>
<p>Total: $%.2f</p>
</div>
<div class="footer">
<p>Thank you for your business!</p>
<p>%s</p>
</div>
</body>
</html>
""",
variables.get("primaryColor"),
variables.get("primaryColor"),
variables.get("companyName"),
variables.get("companyTagline"),
variables.get("invoiceNumber"),
variables.get("invoiceDate"),
variables.get("dueDate"),
variables.get("companyAddress"),
variables.get("companyPhone"),
variables.get("companyEmail"),
variables.get("customerName"),
variables.get("customerAddress"),
variables.get("customerPhone"),
variables.get("itemsHtml"),
variables.get("subtotal"),
variables.get("taxRate"),
variables.get("taxAmount"),
variables.get("total"),
variables.get("footerText")
);
}
}
2. Dynamic Content Generation
Create a service to handle variable substitution and content generation:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ArrayList;
public class InvoicePdfService {
public Map<String, Object> createInvoiceVariables(InvoiceData invoice) {
Map<String, Object> variables = new HashMap<>();
// Company branding
variables.put("primaryColor", "#2563eb");
variables.put("companyName", "LetMePDF Solutions");
variables.put("companyTagline", "Professional PDF Generation Services");
variables.put("companyAddress", "123 Business Street, Tech City, TC 12345");
variables.put("companyPhone", "+1 (555) 123-4567");
variables.put("companyEmail", "contact@letmepdf.com");
// Invoice details
variables.put("invoiceNumber", invoice.getInvoiceNumber());
variables.put("invoiceDate", formatDate(invoice.getInvoiceDate()));
variables.put("dueDate", formatDate(invoice.getDueDate()));
// Customer information
variables.put("customerName", invoice.getCustomerName());
variables.put("customerAddress", invoice.getCustomerAddress());
variables.put("customerPhone", invoice.getCustomerPhone());
// Items and calculations
variables.put("itemsHtml", generateItemsHtml(invoice.getItems()));
variables.put("subtotal", calculateSubtotal(invoice.getItems()));
variables.put("taxRate", invoice.getTaxRate());
variables.put("taxAmount", calculateTax(invoice.getItems(), invoice.getTaxRate()));
variables.put("total", calculateTotal(invoice.getItems(), invoice.getTaxRate()));
// Footer
variables.put("footerText", "Payment due within 30 days. Late payments subject to 1.5%% monthly interest.");
return variables;
}
private String generateItemsHtml(List<InvoiceItem> items) {
StringBuilder html = new StringBuilder();
for (InvoiceItem item : items) {
html.append(String.format("""
<tr>
<td>%s</td>
<td>%d</td>
<td>$%.2f</td>
<td>$%.2f</td>
</tr>
""",
item.getDescription(),
item.getQuantity(),
item.getUnitPrice(),
item.getQuantity() * item.getUnitPrice()
));
}
return html.toString();
}
private double calculateSubtotal(List<InvoiceItem> items) {
return items.stream()
.mapToDouble(item -> item.getQuantity() * item.getUnitPrice())
.sum();
}
private double calculateTax(List<InvoiceItem> items, double taxRate) {
return calculateSubtotal(items) * (taxRate / 100.0);
}
private double calculateTotal(List<InvoiceItem> items, double taxRate) {
return calculateSubtotal(items) + calculateTax(items, taxRate);
}
private String formatDate(LocalDate date) {
return date.format(DateTimeFormatter.ofPattern("MMMM dd, yyyy"));
}
}
3. Complete Usage Example
Here's how to use the advanced PDF generator:
public class AdvancedPdfExample {
public static void main(String[] args) {
// Create sample invoice data
InvoiceData invoice = new InvoiceData();
invoice.setInvoiceNumber("INV-2025-001");
invoice.setInvoiceDate(LocalDate.now());
invoice.setDueDate(LocalDate.now().plusDays(30));
invoice.setCustomerName("John Doe");
invoice.setCustomerAddress("456 Customer Ave, Client City, CC 67890");
invoice.setCustomerPhone("+1 (555) 987-6543");
invoice.setTaxRate(8.5);
List<InvoiceItem> items = new ArrayList<>();
items.add(new InvoiceItem("Premium PDF Generation Service", 1, 299.99));
items.add(new InvoiceItem("Custom Template Design", 2, 150.00));
items.add(new InvoiceItem("API Integration Support", 1, 199.99));
invoice.setItems(items);
// Generate PDF
InvoicePdfService pdfService = new InvoicePdfService();
AdvancedPdfGenerator generator = new AdvancedPdfGenerator();
Map<String, Object> variables = pdfService.createInvoiceVariables(invoice);
generator.generatePdf(variables, "professional-invoice.pdf");
System.out.println("✅ Professional invoice PDF generated!");
}
}
🎨 Customization Options
Brand Customization
// Different brand themes
Map<String, Object> modernTheme = Map.of(
"primaryColor", "#3b82f6",
"fontFamily", "'Inter', sans-serif"
);
Map<String, Object> corporateTheme = Map.of(
"primaryColor", "#1f2937",
"fontFamily", "'Roboto', sans-serif"
);
Layout Customization
// Custom page sizes and margins
page.pdf(new Page.PdfOptions()
.setFormat("Letter")
.setMargin(new Page.PdfOptions.Margin()
.setTop("0.5in")
.setBottom("0.5in")
.setLeft("0.75in")
.setRight("0.75in"))
.setPrintBackground(true));
🚀 Production-Ready Alternative
For production environments, consider LetMePDF.com which offers:
- Template Management: Pre-built templates with variable substitution
- Brand Consistency: Centralized styling and branding
- API Integration: Simple REST API for PDF generation
- Scalability: Handles high-volume PDF generation
Example API usage with custom variables:
curl -X POST https://api.letmepdf.com/html-to-pdf \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"html": "<h1>{{company_name}}</h1><p>Invoice #{{invoice_number}}</p>",
"variables": {
"company_name": "LetMePDF Solutions",
"invoice_number": "INV-2025-001"
},
"options": {
"format": "A4",
"margin": "1in",
"printBackground": true
}
}' --output invoice.pdf
✅ Best Practices
- Template Management: Store HTML templates separately from code
- Variable Validation: Always validate custom variables before substitution
- Error Handling: Implement proper error handling for missing variables
- Performance: Cache browser instances for high-volume generation
- Testing: Test PDF generation with various data scenarios
🎯 Conclusion
Custom variables in Playwright Java enable you to create professional, branded PDFs with dynamic content. This approach gives you complete control over styling, layout, and content generation.
For teams that need enterprise-grade PDF generation without the infrastructure overhead, LetMePDF.com provides a managed solution with advanced template features and variable substitution.
Start building professional PDFs today with Playwright Java, or explore LetMePDF's API for a production-ready alternative!
Further Reading: