jspdf-md-renderer
v4.1.0
Published
A jsPDF utility to render Markdown directly into formatted PDFs with custom designs.
Maintainers
Readme
jsPDF Markdown Renderer
A utility to render Markdown directly into formatted PDFs using jsPDF.
Highlights
- Rich markdown support (headings, lists, tables, images, code, blockquotes, links)
- Configurable typography, spacing, and block styling
- Header/footer and page-number support
- Safe inline layout and long-token wrapping
- Optional security enforcement for untrusted markdown
Installation
npm install jspdf-md-rendererQuick Start
import { jsPDF } from 'jspdf'
import { MdTextRender } from 'jspdf-md-renderer'
const markdown = `
# Project Report
This report includes **formatted markdown** content.
- Item 1
- Item 2
`
const doc = new jsPDF({ unit: 'mm', format: 'a4', orientation: 'portrait' })
await MdTextRender(doc, markdown, {
cursor: { x: 10, y: 10 },
page: {
format: 'a4',
unit: 'mm',
orientation: 'portrait',
maxContentWidth: 190,
maxContentHeight: 277,
lineSpace: 1.5,
defaultLineHeightFactor: 1.2,
defaultFontSize: 12,
defaultTitleFontSize: 14,
topmargin: 10,
xpading: 10,
xmargin: 10,
indent: 10,
},
font: {
bold: { name: 'helvetica', style: 'bold' },
regular: { name: 'helvetica', style: 'normal' },
light: { name: 'helvetica', style: 'light' },
},
endCursorYHandler: (y) => {
console.log('Final Y:', y)
},
})
doc.save('report.pdf')Browser Usage
Bundler (Vite/Webpack/Rollup)
import { jsPDF } from 'jspdf'
import 'jspdf-autotable'
import { MdTextRender } from 'jspdf-md-renderer'Script Tag (UMD)
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf@latest/dist/jspdf.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf-autotable@latest/dist/jspdf.plugin.autotable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf-md-renderer@latest/dist/index.umd.js"></script>
<script>
const { jsPDF } = window.jspdf;
const { MdTextRender } = window.JspdfMdRenderer;
</script>Supported Markdown
- Headings (
#to######) - Paragraphs
- Ordered/unordered/task lists
- Links
- Images (with optional
{width,height,align}attributes) - Tables
- Code blocks and inline code
- Blockquotes
- Horizontal rules
- Inline styles (bold/italic)
Key Render Options
const options = {
heading: {
bold: true,
h1: 26,
h2: 22,
h3: 18,
bottomSpacing: 3,
},
list: {
bulletChar: '\u2022 ',
indentSize: 8,
itemSpacing: 0,
},
paragraph: {
bottomSpacing: 3,
color: '#111827',
},
blockquote: {
barColor: '#4A90D9',
barWidth: 2,
paddingLeft: 6,
backgroundColor: '#F8FAFC',
},
codeBlock: {
backgroundColor: '#F6F8FA',
borderColor: '#E1E4E8',
borderRadius: 3,
padding: 5,
showLanguageLabel: true,
textColor: '#111827',
},
spacing: {
afterHeading: 2,
afterParagraph: 4,
afterCodeBlock: 4,
afterBlockquote: 3,
afterImage: 2,
afterHR: 2,
betweenListItems: 0,
afterList: 3,
afterTable: 3,
},
header: {
text: 'My Report',
align: 'center',
color: '#6b7280',
fontSize: 9,
},
footer: {
showPageNumbers: true,
align: 'right',
},
}Behavior notes:
- Heading size fallback:
heading.hN->page.defaultTitleFontSize heading.bolddefaults totrue- List spacing precedence:
spacing.betweenListItems>list.itemSpacing - Table width follows
page.maxContentWidthfor consistent block layout
Security Controls (opt-in)
Security is disabled by default for backward compatibility.
const options = {
// ...other options
security: {
enabled: true,
violationMode: 'skip', // 'skip' | 'throw' | 'placeholder'
placeholderText: '[blocked]',
placeholderImageText: '[blocked image]',
// Link controls
allowedLinkProtocols: ['https:', 'http:', 'mailto:', 'tel:'],
disablePdfLinks: false,
// Image controls
allowRemoteImages: true,
allowedImageProtocols: ['https:', 'http:'],
allowedImageDomains: ['cdn.example.com'],
allowDataUrls: true,
allowSvgImages: true,
// SSRF controls
blockLocalhost: true,
blockPrivateIPs: true,
blockLinkLocalIPs: true,
blockMetadataIPs: true,
// Limits
maxMarkdownLength: 500000,
maxImageCount: 200,
maxImageSizeBytes: 10 * 1024 * 1024,
maxNestedDepth: 20,
renderTimeoutMs: 30000,
// Hooks
validateUrl: async (url, type) => true,
onSecurityViolation: (violation) => console.warn(violation),
},
}Security Details
- URL classes:
- explicit scheme (
https://...) -> full protocol/domain/IP checks - protocol-relative (
//host/path) -> treated as external absolute URL - relative path (
/a,./a,?q=1,#id) -> allowed by default unless custom validator rejects
- explicit scheme (
allowedImageDomainssemantics:undefined-> allow all domains[]-> deny all domains
maxImageSizeBytesuses decoded bytes for data URLsSecurityViolationErroris exported for throw-mode handling
Browser caveat:
- IP-level SSRF checks are best-effort in browser runtime due to DNS API limitations.
- For strict SSRF policy, fetch remote images through a trusted server-side proxy.
API Exports
MdTextRenderMdTextParserSecurityViolationErrorvalidateOptions- Types:
RenderOption,RenderSecurityOptions,SecurityViolation,ViolationMode, and others
Examples and Docs
- Docs site: https://jeelgajera.github.io/jspdf-md-renderer/
- Resume example: https://jeelgajera.github.io/jspdf-md-renderer/examples/resume
- Invoice example: https://jeelgajera.github.io/jspdf-md-renderer/examples/invoice
- Technical report example: https://jeelgajera.github.io/jspdf-md-renderer/examples/report
Contributing
Contributions are welcome. Please read CONTRIBUTING.md.
License
MIT. See LICENSE.
