How to Debug html-to-pdfmake Conversion Issues: A Complete Troubleshooting Guide
To debug html-to-pdfmake conversion issues, inspect the parsed DOM before conversion, verify your options object, and trace the recursive walk through parseElement while checking style aggregation in applyStyle and parseStyle.
The html-to-pdfmake library transforms HTML strings into pdfmake document definitions, but when the output doesn't match expectations, debugging requires understanding the three-phase conversion pipeline in index.js. Whether you're troubleshooting missing styles, broken tables, or disappearing images, this guide provides systematic steps to debug html-to-pdfmake conversion issues using the actual source code implementation.
Verify Input HTML and DOM Parsing
The conversion begins by parsing your HTML string using the browser's native DOMParser (lines 21-31 in index.js). Errors here propagate silently or produce unexpected node structures.
Check for Malformed HTML
Stray whitespace, unclosed tags, or HTML entities can cause the parser to insert unexpected nodes. Before calling htmlToPdfMake, test the parsed DOM directly:
const html = '<div style="display:none">Hidden</div><p> spaced text </p>';
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
console.log(doc.body.innerHTML); // Inspect the normalized structure
Verify Hidden Elements and Whitespace Handling
Two options control initial filtering:
showHidden: Set totrueto include elements withdisplay:none(default isfalse)removeExtraBlanks: Set tofalseto preserve whitespace in<pre>or<code>blocks
const htmlToPdfMake = require('html-to-pdfmake');
const doc = htmlToPdfMake(html, {
showHidden: true,
removeExtraBlanks: false
});
Inspect the Options Object
The options object (lines 13-20) directly influences how parseElement and applyStyle behave. Misconfiguration here is a common source of silent failures.
Default Styles Override
The defaultStyles option merges with the built-in style map (lines 58-79). Setting a key to null removes that style entirely, but forgetting to spread existing defaults can wipe unintended styles:
// Removes bold styling from <b> tags only
const doc = htmlToPdfMake('<b>no bold</b>', {
defaultStyles: { b: null }
});
Table Auto-Sizing Pitfalls
When tableAutoSize is true, the library calculates column widths from width attributes or percentage-based CSS. If the <table> lacks a width="100%" attribute, the auto-sizing logic may skip percentage calculations (line 194).
const html = `
<table width="100%">
<tr><td style="width:30%">A</td><td style="width:70%">B</td></tr>
</table>`;
const doc = htmlToPdfMake(html, { tableAutoSize: true });
Image Reference Mode
With imagesByReference: true, the function returns an object {content, images} instead of a direct document definition. Forgetting to access result.images leads to missing images in the final PDF (lines 166-171).
const result = htmlToPdfMake('<img src="logo.png">', {
imagesByReference: true
});
console.log(result.images); // Check the generated reference keys
Custom Tag Handler Errors
The customTag callback receives {element, parents, ret} but errors inside this function are silently caught. Add defensive logging:
const doc = htmlToPdfMake('<my-box></my-box>', {
customTag: ({element, parents, ret}) => {
try {
if (element.tagName === 'MY-BOX') {
return {
canvas: [{ type: 'rect', x: 0, y: 0, w: 200, h: 50 }],
margin: [0, 10, 0, 10]
};
}
} catch (e) {
console.error('Custom tag error:', e);
}
return ret;
}
});
Trace the Recursive Element Walker
The core conversion logic resides in parseElement (lines 41-95), which recursively processes each DOM node. Understanding this walk helps diagnose structural issues.
Stack vs Text Detection
The searchForStack function (lines 37-44) determines whether a node should become a stack (container) or text node. Block-level elements like <div> or <p> trigger stack mode, while inline elements remain text.
If elements appear concatenated on a single line when they should stack vertically, check that the parent container is recognized as a block element:
// Debug the node type detection
const html = '<div><p>Line 1</p><p>Line 2</p></div>';
const doc = htmlToPdfMake(html);
console.log(JSON.stringify(doc, null, 2));
// Should show nested stacks, not flat text array
Temporary Debug Logging
Insert a temporary log at the top of parseElement to trace the traversal:
// Inside node_modules/html-to-pdfmake/index.js, temporarily add:
if (element.nodeName) {
console.log('Parsing', element.nodeName,
'parent chain:', parents.map(p => p.nodeName));
}
This reveals the exact path taken through the DOM and helps identify where the walker diverges from expectations.
Debug Style Aggregation
Styles flow through three layers: default styles (lines 58-79), inline style attributes, and CSS classes. The applyStyle (lines 55-90) and parseStyle (lines 68-112) functions handle this transformation.
CSS-to-pdfmake Property Mapping
Not all CSS properties map directly to pdfmake. Key mappings include:
background-color→fillColor(for table cells)text-decoration: underline→decoration: 'underline'font-weight: bold→bold: true- Margins/paddings → arrays
[left, top, right, bottom]in points
Common Style Bugs
RGBA Opacity Dropped: When using rgba() colors with alpha < 1, the opacity is lost unless fillOpacity is explicitly set. Use solid hex colors (#ff0000) for reliable rendering.
Margin Auto Ignored: The parser silently drops margin: auto (line 334 in parseStyle). Always specify concrete units:
// Bad
<div style="margin: auto">
// Good
<div style="margin: 20pt">
Inherited Underlines: Text decoration properties inherit differently than other styles. If a parent <div> has text-decoration: underline, child text nodes may inherit this unexpectedly depending on the removeTagClasses setting.
Inspecting Computed Styles
Always serialize the final document definition to verify style transformation:
const doc = htmlToPdfMake('<p style="margin:2cm;color:#0f0">Test</p>');
console.log(JSON.stringify(doc, null, 2));
// Expected output shows margin converted to points: [56.6929,56.6929,56.6929,56.6929]
// And color normalized to: "#00ff00"
Troubleshoot Table Rendering
Tables undergo complex post-processing for column spans, row spans, and width calculations (lines 95-99, 115-130).
Colspan and Rowspan Handling
The library processes colspan and rowspan attributes during the stack-building phase (lines 135-147). If cells appear misaligned or missing:
- Verify that
colSpanandrowSpanare lowercase in your HTML (the parser checks these attributes specifically) - Ensure table rows (
<tr>) are direct children of<table>, not wrapped in intermediate<tbody>tags that might confuse the walker
Table Auto-Sizing Logic
When tableAutoSize: true, the library extracts widths from:
widthattributes on<table>and<col>elements- Percentage values in
style="width:X%"
Critical requirement: The <table> must have width="100%" attribute for percentage-based column widths to calculate correctly (line 194). Without this, the library cannot determine the relative proportions.
Data Attributes for Advanced Control
Use data-pdfmake to inject raw pdfmake properties:
const html = `
<table data-pdfmake='{"layout":"noBorders","dontBreakRows":true}' width="100%">
<tr><th colspan="2">Header</th></tr>
<tr><td rowspan="2">A</td><td>B</td></tr>
<tr><td>C</td></tr>
</table>`;
const docDef = htmlToPdfMake(html, {tableAutoSize:true});
console.log(JSON.stringify(docDef.table.layout)); // "noBorders"
If data-pdfmake JSON is malformed, the library logs to console.error but continues processing, potentially ignoring your custom settings.
Verify Image Handling
Images support two processing modes controlled by the imagesByReference option (lines 166-171).
Embedded vs Reference Mode
Embedded mode (default): Returns image nodes with src containing data URLs or file paths directly in the content tree.
Reference mode (imagesByReference: true): Returns an object with content and images properties. Image URLs are replaced with reference keys (img_ref_<suffix><index>) and the actual data is stored in the images map.
Debugging Image Paths
When images appear missing in the final PDF:
- Check the return structure: If using
imagesByReference: true, ensure you're accessingresult.images, not justresultorresult.content.
const result = htmlToPdfMake('<img src="logo.png">', {
imagesByReference: true
});
// Correct way to inspect
console.log('Content:', result.content);
console.log('Images map:', result.images);
// { img_ref_xxxxxx0: 'data:image/png;base64,...' }
-
Verify URL accessibility: The library handles both data URLs and relative paths, but the final PDF generation (performed by pdfmake, not html-to-pdfmake) requires that image URLs be resolvable in the target environment (Node.js filesystem or browser blob URLs).
-
Check for JSON string wrapping: The parser handles cases where
srcattributes contain JSON-encoded strings, but verify your HTML doesn't contain double-encoded entities.
Summary
- Start with the input: Verify your HTML parses correctly in a browser's
DOMParserand check thatshowHiddenandremoveExtraBlanksoptions match your content needs. - Audit the options: Misconfigured
defaultStyles,tableAutoSize, orimagesByReferencesettings are common culprits for missing formatting or content. - Trace the recursion: Add temporary logging inside
parseElementto follow the DOM traversal and verify thatsearchForStackcorrectly identifies block-level containers. - Inspect style aggregation: Use
JSON.stringifyon the output to verify thatapplyStyleandparseStylecorrectly transformed CSS properties like margins, colors, and decorations. - Validate tables and images: Ensure tables have proper
widthattributes for auto-sizing and that image references are correctly extracted when usingimagesByReferencemode.
Frequently Asked Questions
Why are my CSS styles not appearing in the PDF output?
Styles flow through applyStyle and parseStyle in index.js, which map CSS properties to pdfmake equivalents. If styles are missing, verify that you haven't set removeTagClasses: true (which removes the auto-generated html-tag classes that carry custom CSS), check that ignoreStyles doesn't include your property, and ensure you're using supported units (px, pt, cm, rem) rather than auto or unsupported CSS variables.
How do I fix tables that render with incorrect column widths?
Tables require explicit width handling in html-to-pdfmake. First, ensure tableAutoSize: true is set in your options. Then verify that your <table> tag includes a width="100%" attribute (line 194 in index.js), as percentage-based column widths cannot calculate without this reference. For complex layouts, use <colgroup> with <col width="X%"> elements placed before any <tr> rows, or inject raw pdfmake properties via data-pdfmake='{"widths":["*","auto"]}'.
Why are images missing from my generated PDF?
Images disappear when the reference map isn't properly handled or when URLs are inaccessible. If using imagesByReference: true, remember that the function returns {content, images} rather than a direct document definition—you must merge result.images into your pdfmake definition's images property. For embedded mode, verify that src attributes contain valid data URLs or resolvable paths, and check the browser console for CORS errors or 404s when loading external images.
How can I debug custom tag handlers that aren't working?
The customTag callback receives {element, parents, ret} but errors inside this function are silently swallowed by the library's error handling. To debug, wrap your custom logic in a try/catch block with explicit console.error logging. Also verify that removeTagClasses isn't true, as this removes the html-tag classes that your handler might rely on for element identification. Test with simple HTML first, then gradually add complexity to isolate the failure point.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →