Developers using iText 7's pdfHTML add-on find that HTML tables render with incorrect column widths, collapsed cells, or misaligned content. Tables that display correctly in browsers produce broken layouts in the generated PDF. This common issue affects invoice templates, reports, and data-heavy documents. This article documents the table rendering limitations and examines alternatives with browser-accurate table handling.
The Problem
When converting HTML containing tables to PDF with iText pdfHTML, the table layout often differs significantly from browser rendering:
- Column widths are ignored or calculated incorrectly
-
colspanandrowspanproduce unexpected results - Table borders may not render correctly
- Cell padding and spacing differ from browser output
- Content overflows or is truncated
- Nested tables collapse or misalign
- Percentage-based widths behave unpredictably
The challenge is that there are typically no error messages to indicate what went wrong. The conversion completes successfully, but the output does not match what developers see in their browsers.
Common Table Rendering Failures
Percentage Width Issues
<!-- Works in browser, broken in pdfHTML -->
<table style="width: 100%">
<tr>
<td style="width: 30%">Column 1</td>
<td style="width: 70%">Column 2</td>
</tr>
</table>
Expected output: Two columns at 30% and 70% of the page width.
Actual output in pdfHTML:
- Columns with equal widths instead of 30%/70%
- Content wrapping incorrectly
- Table not spanning full page width
Colspan and Rowspan Problems
<table style="width: 100%; border-collapse: collapse;">
<tr>
<th colspan="3" style="background: #333; color: white;">Invoice Header</th>
</tr>
<tr>
<td rowspan="2" style="width: 20%;">Item Details</td>
<td style="width: 40%;">Description</td>
<td style="width: 40%;">Amount</td>
</tr>
<tr>
<td>Additional Info</td>
<td>$100.00</td>
</tr>
</table>
Expected output: Header spans three columns, first cell spans two rows.
Actual output in pdfHTML:
- Colspan may create misaligned columns in subsequent rows
- Rowspan cells may overlap adjacent content
- Border rendering becomes inconsistent
Nested Tables
<table style="width: 100%;">
<tr>
<td style="width: 60%;">
<table style="width: 100%;">
<tr>
<td style="width: 50%;">Nested A</td>
<td style="width: 50%;">Nested B</td>
</tr>
</table>
</td>
<td style="width: 40%;">Outer Cell</td>
</tr>
</table>
Expected output: Inner table fills 60% of outer width, with two equal columns.
Actual output in pdfHTML:
- Inner table may collapse to content width
- Percentage calculations become unpredictable
- Layout shifts compared to browser rendering
Tables with Images
<table style="width: 100%;">
<tr>
<td style="width: 100px;"><img src="product.jpg" style="width: 80px;"></td>
<td>Product description text that should wrap correctly...</td>
</tr>
</table>
Expected output: Fixed-width image column, text column fills remaining space.
Actual output in pdfHTML:
- Image may not respect width constraints
- Text column width calculated incorrectly
- Row height may not adjust properly
Who Is Affected
This issue impacts developers creating any document with structured tabular data:
Use Cases:
- Invoices and receipts - Line items, totals, tax breakdowns require precise column alignment
- Financial reports - Balance sheets, P&L statements, budget comparisons depend on correct widths
- Data exports - Database query results, analytics dashboards, log summaries
- Shipping documents - Packing lists, customs forms, address labels with structured fields
- Medical records - Lab results, medication lists, vital signs in tabular format
- Legal documents - Contracts with schedules, term sheets, comparison tables
Industries: Finance, e-commerce, healthcare, legal, logistics, manufacturing, and government sectors all rely heavily on tabular document layouts.
Framework Versions: The issue affects iText 7 pdfHTML across all major versions, including the latest releases. While iText has made incremental improvements to table handling, the fundamental differences from browser rendering remain.
Evidence from the Developer Community
The table rendering discrepancy is one of the most frequently reported issues in iText forums and Stack Overflow discussions.
Community Reports
"Table widths in HTML are not respected in the generated PDF. I have tried fixed widths, percentages, and min-width but nothing works as expected."
— iText Community Forum, 2023"My invoice template works in the browser but the table is completely wrong in the PDF. Columns that should be 30% are rendering at 50%."
— Developer report, Stack Overflow"Nested tables are completely broken. The inner table ignores its parent cell width entirely."
— GitHub issue comment, 2022"We spent weeks trying to get our financial reports to render correctly. Eventually we gave up and switched to a different library."
— Reddit r/java discussion
Comparison: Browser vs pdfHTML Output
Developers frequently describe the difference between expected and actual output. A table designed for a standard invoice layout:
| Element | Browser Behavior | pdfHTML Behavior |
|---|---|---|
width: 100% on table |
Fills container width | Often narrower than expected |
width: 30% on column |
30% of table width | Varies, often equal distribution |
colspan |
Spans specified columns | May misalign subsequent rows |
border-collapse |
Collapses borders correctly | Inconsistent border rendering |
| Nested tables | Respects parent constraints | Often ignores parent width |
Root Cause Analysis
pdfHTML uses a custom table layout algorithm that differs from browser implementations. Understanding why this happens helps explain why workarounds are often unsatisfactory.
The CSS Table Layout Specification
Browsers implement the CSS 2.1 table layout algorithm, which involves:
- Automatic table layout: Calculate column widths based on content and specified widths
- Fixed table layout: Use specified widths without content-based adjustments
- Intrinsic sizing: Determine minimum and maximum content widths for each cell
- Constraint distribution: Distribute excess space among columns
This algorithm is complex and has evolved over decades of browser development. pdfHTML implements an approximation that works for simple cases but diverges for complex layouts.
Specific Technical Differences
Percentage Width Resolution: Browsers resolve percentage widths relative to the containing block, with specific rules for how nested percentages compound. pdfHTML's calculation differs, particularly for nested structures.
Content-Based Sizing: Browsers calculate minimum content width (longest unbreakable word) and maximum content width (no line breaks). pdfHTML's content measurement can produce different results.
Border Collapse Model: The CSS
border-collapseproperty triggers specific border rendering rules that browsers implement consistently. pdfHTML's border rendering may not match.Flex Item Tables: Tables inside flex containers have specific sizing behavior that pdfHTML does not implement (since it lacks flexbox support).
Why This Is Hard to Fix
Implementing a complete CSS table layout engine requires essentially building a browser rendering engine. iText's core purpose is PDF manipulation, not web rendering. The pdfHTML module is a commercial add-on that provides "good enough" HTML support for many use cases, but browser-perfect rendering is not its design goal.
Attempted Workarounds
Developers have tried several approaches to work around table rendering issues.
Workaround 1: Use Fixed Pixel Widths
Approach: Replace percentage widths with absolute pixel values calculated for the specific page size.
<!-- A4 page at 72 DPI = 595 points wide, minus margins -->
<table style="width: 515px">
<tr>
<td style="width: 154px">Column 1 (30%)</td>
<td style="width: 361px">Column 2 (70%)</td>
</tr>
</table>
Limitations:
- Breaks responsive design for web viewing
- Requires calculating page-specific widths for each page size
- Necessitates maintaining separate templates for web and PDF
- Does not solve colspan/rowspan issues
Workaround 2: Simplify Table Structure
Approach: Flatten nested tables and avoid complex spanning cells.
<!-- Instead of nested table for layout -->
<table style="width: 100%;">
<tr>
<td style="width: 33%;">Section 1</td>
<td style="width: 33%;">Section 2</td>
<td style="width: 34%;">Section 3</td>
</tr>
</table>
Limitations:
- Limits design flexibility significantly
- May not adequately represent complex data relationships
- Requires redesigning existing templates that work in browsers
- Still may not render correctly for all cases
Workaround 3: Use colgroup for Column Definitions
Approach: Define column widths explicitly with colgroup elements.
<table style="width: 100%;">
<colgroup>
<col style="width: 30%;">
<col style="width: 70%;">
</colgroup>
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</table>
Limitations:
- May or may not improve rendering depending on pdfHTML version
- Adds complexity to templates
- Does not address all table rendering issues
Workaround 4: Post-Process with iText API
Approach: Convert to PDF, then manipulate table elements programmatically using iText's low-level API.
// After HTML conversion, adjust table widths manually
PdfDocument pdf = new PdfDocument(new PdfWriter(outputStream));
Document document = new Document(pdf);
// Abandon HTML conversion, build tables programmatically
Table table = new Table(UnitValue.createPercentArray(new float[]{30, 70}));
table.setWidth(UnitValue.createPercentValue(100));
table.addCell(new Cell().add(new Paragraph("Column 1")));
table.addCell(new Cell().add(new Paragraph("Column 2")));
document.add(table);
Limitations:
- Complex and error-prone implementation
- Defeats the purpose of HTML-to-PDF conversion
- Requires significant additional development
- Mixes HTML conversion with programmatic PDF construction
A Different Approach: IronPDF
IronPDF uses Chromium's table rendering engine, producing PDF tables identical to browser output. This is not an approximation or custom parser. It uses the actual browser engine that developers test their HTML against.
Why IronPDF Tables Match Browsers
IronPDF's Chromium engine:
- Same layout algorithms as Chrome: The exact code that calculates table widths in Chrome calculates them in IronPDF
- Percentage widths work correctly: 30%/70% columns render as 30%/70% of the container
- Colspan and rowspan: Complex spanning cells align correctly in all cases
- Nested tables: Inner tables respect outer cell constraints
- Border collapse: CSS border rules apply exactly as specified
Code Example: Invoice with Complex Table
using IronPdf;
public class InvoiceGenerator
{
public byte[] GenerateInvoice(InvoiceData invoice)
{
var renderer = new ChromePdfRenderer();
string html = $@"
<!DOCTYPE html>
<html>
<head>
<style>
body {{ font-family: 'Segoe UI', Arial, sans-serif; padding: 40px; }}
table {{
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}}
th, td {{
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}}
th {{
background-color: #2c3e50;
color: white;
}}
.description {{ width: 50%; }}
.quantity {{ width: 15%; text-align: center; }}
.price {{ width: 15%; text-align: right; }}
.total {{ width: 20%; text-align: right; }}
tfoot td {{
font-weight: bold;
border-top: 2px solid #2c3e50;
}}
</style>
</head>
<body>
<h1>Invoice #{invoice.Number}</h1>
<table>
<thead>
<tr>
<th class='description'>Description</th>
<th class='quantity'>Qty</th>
<th class='price'>Unit Price</th>
<th class='total'>Total</th>
</tr>
</thead>
<tbody>
{GenerateLineItems(invoice.Items)}
</tbody>
<tfoot>
<tr>
<td colspan='3'>Subtotal</td>
<td class='total'>${invoice.Subtotal:F2}</td>
</tr>
<tr>
<td colspan='3'>Tax (10%)</td>
<td class='total'>${invoice.Tax:F2}</td>
</tr>
<tr>
<td colspan='3'>Total</td>
<td class='total'>${invoice.Total:F2}</td>
</tr>
</tfoot>
</table>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
private string GenerateLineItems(IEnumerable<LineItem> items)
{
return string.Join("", items.Select(item => $@"
<tr>
<td class='description'>{item.Description}</td>
<td class='quantity'>{item.Quantity}</td>
<td class='price'>${item.UnitPrice:F2}</td>
<td class='total'>${item.Total:F2}</td>
</tr>"));
}
}
Code Example: Nested Tables
public byte[] GenerateNestedTableDocument()
{
var renderer = new ChromePdfRenderer();
string html = @"
<!DOCTYPE html>
<html>
<head>
<style>
table { width: 100%; border-collapse: collapse; }
td, th { padding: 10px; border: 1px solid #ccc; }
.outer { background: #f5f5f5; }
.inner { background: white; margin: 5px; }
.inner td { border: 1px solid #ddd; }
</style>
</head>
<body>
<table class='outer'>
<tr>
<td style='width: 60%;'>
<h3>Product Details</h3>
<table class='inner'>
<tr>
<td style='width: 40%;'>SKU</td>
<td style='width: 60%;'>PRD-12345</td>
</tr>
<tr>
<td>Category</td>
<td>Electronics</td>
</tr>
<tr>
<td>Weight</td>
<td>2.5 kg</td>
</tr>
</table>
</td>
<td style='width: 40%; vertical-align: top;'>
<h3>Pricing</h3>
<table class='inner'>
<tr>
<td style='width: 50%;'>List Price</td>
<td style='width: 50%;'>$299.99</td>
</tr>
<tr>
<td>Discount</td>
<td>-$30.00</td>
</tr>
<tr>
<td><strong>Final</strong></td>
<td><strong>$269.99</strong></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
Code Example: Rowspan for Multi-Row Cells
public byte[] GenerateRowspanTable()
{
var renderer = new ChromePdfRenderer();
string html = @"
<!DOCTYPE html>
<html>
<head>
<style>
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px; border: 1px solid #333; }
th { background: #2c3e50; color: white; }
.category { background: #ecf0f1; font-weight: bold; }
</style>
</head>
<body>
<table>
<thead>
<tr>
<th style='width: 20%;'>Category</th>
<th style='width: 40%;'>Product</th>
<th style='width: 20%;'>Price</th>
<th style='width: 20%;'>Stock</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan='3' class='category'>Electronics</td>
<td>Laptop Pro 15</td>
<td>$1,299</td>
<td>45</td>
</tr>
<tr>
<td>Wireless Mouse</td>
<td>$49</td>
<td>200</td>
</tr>
<tr>
<td>USB-C Hub</td>
<td>$79</td>
<td>150</td>
</tr>
<tr>
<td rowspan='2' class='category'>Accessories</td>
<td>Laptop Bag</td>
<td>$89</td>
<td>75</td>
</tr>
<tr>
<td>Screen Protector</td>
<td>$29</td>
<td>300</td>
</tr>
</tbody>
</table>
</body>
</html>";
using var pdf = renderer.RenderHtmlAsPdf(html);
return pdf.BinaryData;
}
Key points:
- Percentage widths work as expected in all scenarios
-
colspanrenders correctly with proper column alignment -
rowspancells span multiple rows without overlapping adjacent content - Nested tables respect parent container widths
- Same template works for web preview and PDF generation
API Reference
Migration Considerations
Licensing
- iText pdfHTML is a commercial add-on
- IronPDF is commercial with perpetual licensing
- IronPDF Licensing
API Differences
- iText:
HtmlConverter.ConvertToPdf() - IronPDF:
ChromePdfRenderer.RenderHtmlAsPdf()
What You Gain
- Browser-accurate table rendering
- Percentage widths work correctly
- Complex table structures supported
- Same template for web and PDF
What to Consider
- Different API surface
- Chromium-based vs custom renderer
- Commercial licensing for both
Conclusion
iText pdfHTML's table rendering differs from browser implementations, breaking layouts that work correctly in HTML. Workarounds require abandoning standard HTML/CSS patterns or manual post-processing. For documents with complex tables, browser-based rendering engines provide the layout accuracy that custom parsers cannot match.
Written by Jacob Mellor, CTO at Iron Software.
References
- iText Community - Table rendering discussions
For the latest IronPDF documentation and tutorials, visit ironpdf.com.
Top comments (0)