No articles found
Try different keywords or browse our categories
Angular 21 jsPDF Example to Edit & Modify PDF Files in Browser
Learn how to integrate jsPDF with Angular 21 to create, edit, and modify PDF documents directly in the browser using standalone components and signals.
Comprehensive guide to using jsPDF in Angular 21 applications with standalone components, signals, and modern TypeScript features for PDF generation and manipulation.
Installation
Install jsPDF and its types in your Angular project:
npm install jspdf
npm install --save-dev @types/jspdf
For HTML to PDF conversion, also install html2canvas:
npm install html2canvas
Basic Standalone Component
Create a simple PDF generator using Angular 21 standalone components:
// pdf-generator.component.ts
import { Component } from '@angular/core';
import jsPDF from 'jspdf';
@Component({
selector: 'app-pdf-generator',
standalone: true,
template: `
<div class="container">
<h2>PDF Generator</h2>
<button (click)="generatePDF()" class="btn">
Download PDF
</button>
</div>
`,
styles: [`
.container {
padding: 20px;
}
.btn {
background: #dd0031;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
background: #c3002f;
}
`]
})
export class PdfGeneratorComponent {
generatePDF(): void {
const doc = new jsPDF();
doc.text('Hello from Angular 21!', 20, 20);
doc.save('document.pdf');
}
}
Using Angular Signals
Leverage Angular 21’s signals for reactive PDF generation:
import { Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import jsPDF from 'jspdf';
@Component({
selector: 'app-pdf-form',
standalone: true,
imports: [FormsModule],
template: `
<div class="pdf-form">
<h2>Create Your PDF</h2>
<input
[(ngModel)]="title"
placeholder="Enter title"
class="input"
/>
<textarea
[(ngModel)]="content"
placeholder="Enter content"
rows="5"
class="textarea"
></textarea>
<button (click)="createPDF()" class="btn">
Create PDF
</button>
</div>
`,
styles: [`
.pdf-form {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.input, .textarea {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
}
.btn {
background: #dd0031;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
width: 100%;
}
`]
})
export class PdfFormComponent {
title = signal('');
content = signal('');
createPDF(): void {
const doc = new jsPDF();
doc.setFontSize(18);
doc.text(this.title() || 'Untitled', 20, 20);
doc.setFontSize(12);
const lines = doc.splitTextToSize(this.content(), 170);
doc.text(lines, 20, 35);
doc.save(`${this.title() || 'document'}.pdf`);
}
}
PDF Service with Dependency Injection
Create a reusable service for PDF operations:
// pdf.service.ts
import { Injectable } from '@angular/core';
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
export interface PdfOptions {
title: string;
content: string;
filename?: string;
}
@Injectable({
providedIn: 'root'
})
export class PdfService {
createBasicPDF(options: PdfOptions): jsPDF {
const doc = new jsPDF();
doc.setFontSize(18);
doc.text(options.title, 20, 20);
doc.setFontSize(12);
const lines = doc.splitTextToSize(options.content, 170);
doc.text(lines, 20, 35);
return doc;
}
downloadPDF(doc: jsPDF, filename: string = 'document.pdf'): void {
doc.save(filename);
}
previewPDF(doc: jsPDF): void {
const blob = doc.output('blob');
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
}
async elementToPDF(element: HTMLElement, filename: string = 'export.pdf'): Promise<void> {
const canvas = await html2canvas(element);
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF();
const imgWidth = 190;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight);
pdf.save(filename);
}
createStyledPDF(): jsPDF {
const doc = new jsPDF();
// Title
doc.setFontSize(24);
doc.setFont(undefined, 'bold');
doc.text('Professional Document', 105, 20, { align: 'center' });
// Subtitle
doc.setFontSize(14);
doc.setFont(undefined, 'italic');
doc.setTextColor(100, 100, 100);
doc.text('Generated with Angular 21', 105, 30, { align: 'center' });
// Reset to normal
doc.setTextColor(0, 0, 0);
doc.setFont(undefined, 'normal');
doc.setFontSize(12);
// Content
let y = 50;
const content = [
'This PDF demonstrates:',
'',
'• Multiple font sizes and styles',
'• Text alignment and colors',
'• Shapes and graphics',
'• Professional formatting'
];
content.forEach(line => {
doc.text(line, 20, y);
y += 8;
});
// Colored box
y += 10;
doc.setFillColor(221, 0, 49);
doc.rect(20, y, 170, 20, 'F');
doc.setTextColor(255, 255, 255);
doc.setFontSize(13);
doc.text('Angular + jsPDF', 105, y + 12, { align: 'center' });
return doc;
}
}
Use the service in a component:
import { Component, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { PdfService } from './pdf.service';
@Component({
selector: 'app-pdf-creator',
standalone: true,
imports: [FormsModule],
template: `
<div class="container">
<input [(ngModel)]="title" placeholder="Title" class="input" />
<textarea [(ngModel)]="content" placeholder="Content" class="textarea"></textarea>
<div class="button-group">
<button (click)="download()" class="btn btn-primary">
Download
</button>
<button (click)="preview()" class="btn btn-secondary">
Preview
</button>
</div>
</div>
`,
styles: [`
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.input, .textarea {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 4px;
}
.button-group {
display: flex;
gap: 10px;
}
.btn {
flex: 1;
padding: 12px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
background: #dd0031;
color: white;
}
.btn-secondary {
background: #555;
color: white;
}
`]
})
export class PdfCreatorComponent {
private pdfService = inject(PdfService);
title = '';
content = '';
download(): void {
const doc = this.pdfService.createBasicPDF({
title: this.title,
content: this.content
});
this.pdfService.downloadPDF(doc, `${this.title || 'document'}.pdf`);
}
preview(): void {
const doc = this.pdfService.createBasicPDF({
title: this.title,
content: this.content
});
this.pdfService.previewPDF(doc);
}
}
Invoice Generator Component
Complete invoice generator with Angular forms:
import { Component, signal } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { CommonModule } from '@angular/common';
import jsPDF from 'jspdf';
interface Invoice {
clientName: string;
email: string;
invoiceNumber: string;
amount: number;
date: string;
description: string;
}
@Component({
selector: 'app-invoice-generator',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<div class="invoice-container">
<h2>Invoice Generator</h2>
<form [formGroup]="invoiceForm" (ngSubmit)="generateInvoice()">
<div class="form-row">
<div class="form-group">
<label>Client Name *</label>
<input formControlName="clientName" class="input" />
@if (invoiceForm.get('clientName')?.invalid && invoiceForm.get('clientName')?.touched) {
<span class="error">Required</span>
}
</div>
<div class="form-group">
<label>Email *</label>
<input formControlName="email" type="email" class="input" />
@if (invoiceForm.get('email')?.invalid && invoiceForm.get('email')?.touched) {
<span class="error">Valid email required</span>
}
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Invoice Number *</label>
<input formControlName="invoiceNumber" class="input" />
</div>
<div class="form-group">
<label>Date *</label>
<input formControlName="date" type="date" class="input" />
</div>
</div>
<div class="form-group">
<label>Description *</label>
<textarea formControlName="description" class="textarea" rows="3"></textarea>
</div>
<div class="form-group">
<label>Amount *</label>
<input formControlName="amount" type="number" step="0.01" class="input" />
</div>
<button type="submit" [disabled]="invoiceForm.invalid" class="btn">
Generate Invoice PDF
</button>
</form>
@if (generatedCount() > 0) {
<div class="success-message">
✓ Generated {{ generatedCount() }} invoice(s)
</div>
}
</div>
`,
styles: [`
.invoice-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.input, .textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
}
.error {
color: #dd0031;
font-size: 12px;
}
.btn {
width: 100%;
background: #dd0031;
color: white;
padding: 14px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
}
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.success-message {
margin-top: 20px;
padding: 15px;
background: #d4edda;
color: #155724;
border-radius: 4px;
text-align: center;
}
`]
})
export class InvoiceGeneratorComponent {
invoiceForm: FormGroup;
generatedCount = signal(0);
constructor(private fb: FormBuilder) {
this.invoiceForm = this.fb.group({
clientName: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
invoiceNumber: ['', Validators.required],
date: ['', Validators.required],
description: ['', Validators.required],
amount: ['', [Validators.required, Validators.min(0)]]
});
}
generateInvoice(): void {
if (this.invoiceForm.invalid) return;
const invoice: Invoice = this.invoiceForm.value;
const doc = new jsPDF();
let y = 20;
// Company header
doc.setFontSize(22);
doc.setFont(undefined, 'bold');
doc.setTextColor(221, 0, 49);
doc.text('YOUR COMPANY', 20, y);
doc.setFontSize(10);
doc.setTextColor(0, 0, 0);
doc.setFont(undefined, 'normal');
y += 10;
doc.text('123 Business Street, City, State 12345', 20, y);
y += 5;
doc.text('Phone: (555) 123-4567', 20, y);
y += 5;
doc.text('contact@company.com', 20, y);
// Invoice title
y += 15;
doc.setFontSize(24);
doc.setFont(undefined, 'bold');
doc.text('INVOICE', 20, y);
// Invoice details
y += 12;
doc.setFontSize(11);
doc.setFont(undefined, 'normal');
doc.text(`Invoice #: ${invoice.invoiceNumber}`, 20, y);
doc.text(`Date: ${invoice.date}`, 150, y);
// Client info
y += 15;
doc.setFont(undefined, 'bold');
doc.text('BILL TO:', 20, y);
y += 6;
doc.setFont(undefined, 'normal');
doc.text(invoice.clientName, 20, y);
y += 5;
doc.text(invoice.email, 20, y);
// Description table header
y += 20;
doc.setFillColor(240, 240, 240);
doc.rect(20, y - 5, 170, 10, 'F');
doc.setFont(undefined, 'bold');
doc.text('Description', 22, y);
doc.text('Amount', 165, y);
// Description and amount
y += 10;
doc.setFont(undefined, 'normal');
const descLines = doc.splitTextToSize(invoice.description, 120);
doc.text(descLines, 22, y);
doc.text(`$${invoice.amount.toFixed(2)}`, 165, y);
// Total section
y += Math.max(descLines.length * 5, 10) + 10;
doc.line(115, y, 190, y);
y += 8;
doc.setFont(undefined, 'bold');
doc.setFontSize(14);
doc.text('TOTAL:', 130, y);
doc.setFontSize(16);
doc.setTextColor(221, 0, 49);
doc.text(`$${invoice.amount.toFixed(2)}`, 165, y);
// Footer
const pageHeight = doc.internal.pageSize.height;
doc.setFontSize(9);
doc.setTextColor(100, 100, 100);
doc.setFont(undefined, 'italic');
doc.text('Thank you for your business!', 105, pageHeight - 30, { align: 'center' });
doc.text('Payment is due within 30 days', 105, pageHeight - 25, { align: 'center' });
doc.save(`invoice-${invoice.invoiceNumber}.pdf`);
this.generatedCount.update(count => count + 1);
}
}
Export Component to PDF
Capture and export Angular components as PDF:
import { Component, ElementRef, ViewChild, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PdfService } from './pdf.service';
@Component({
selector: 'app-export-component',
standalone: true,
imports: [CommonModule],
template: `
<div class="container">
<div #content class="content-to-export">
<h1>{{ title }}</h1>
<p>{{ description }}</p>
<ul>
@for (item of items; track item) {
<li>{{ item }}</li>
}
</ul>
<div class="statistics">
<div class="stat-card">
<h3>Total Users</h3>
<p class="stat-number">1,234</p>
</div>
<div class="stat-card">
<h3>Active Sessions</h3>
<p class="stat-number">567</p>
</div>
</div>
</div>
<button (click)="exportToPDF()" class="btn">
Export to PDF
</button>
</div>
`,
styles: [`
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.content-to-export {
padding: 30px;
background: white;
border: 1px solid #ddd;
margin-bottom: 20px;
}
.statistics {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
.stat-card {
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
text-align: center;
}
.stat-number {
font-size: 32px;
font-weight: bold;
color: #dd0031;
}
.btn {
width: 100%;
background: #dd0031;
color: white;
padding: 12px;
border: none;
border-radius: 4px;
cursor: pointer;
}
`]
})
export class ExportComponentComponent {
@ViewChild('content') content!: ElementRef<HTMLElement>;
private pdfService = inject(PdfService);
title = 'Export Report';
description = 'This content will be exported to PDF';
items = ['Feature 1', 'Feature 2', 'Feature 3'];
async exportToPDF(): Promise<void> {
await this.pdfService.elementToPDF(
this.content.nativeElement,
'export-report.pdf'
);
}
}
Multi-Page Reports
Generate complex multi-page reports:
import { Component } from '@angular/core';
import jsPDF from 'jspdf';
interface ReportData {
title: string;
sections: Array<{
heading: string;
content: string[];
}>;
}
@Component({
selector: 'app-report-generator',
standalone: true,
template: `
<div class="container">
<button (click)="generateReport()" class="btn">
Generate Multi-Page Report
</button>
</div>
`,
styles: [`
.container {
padding: 20px;
text-align: center;
}
.btn {
background: #dd0031;
color: white;
padding: 14px 28px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
`]
})
export class ReportGeneratorComponent {
generateReport(): void {
const reportData: ReportData = {
title: 'Annual Report 2024',
sections: [
{
heading: 'Executive Summary',
content: [
'This report summarizes the key findings and achievements.',
'Our organization has shown significant growth this year.',
'Key metrics indicate positive trends across all departments.'
]
},
{
heading: 'Financial Overview',
content: [
'Revenue increased by 25% compared to last year.',
'Operating costs were maintained within budget.',
'Profit margins improved by 15%.'
]
},
{
heading: 'Future Outlook',
content: [
'We expect continued growth in the coming year.',
'New initiatives are planned for market expansion.',
'Investment in technology will drive efficiency.'
]
}
]
};
const doc = new jsPDF();
let currentPage = 1;
// Cover page
this.addCoverPage(doc, reportData.title, currentPage);
// Content pages
reportData.sections.forEach((section, index) => {
doc.addPage();
currentPage++;
this.addContentPage(doc, section, currentPage);
});
doc.save('annual-report.pdf');
}
private addCoverPage(doc: jsPDF, title: string, pageNumber: number): void {
const pageWidth = doc.internal.pageSize.width;
const pageHeight = doc.internal.pageSize.height;
// Background
doc.setFillColor(221, 0, 49);
doc.rect(0, 0, pageWidth, pageHeight / 2, 'F');
// Title
doc.setTextColor(255, 255, 255);
doc.setFontSize(32);
doc.setFont(undefined, 'bold');
doc.text(title, pageWidth / 2, pageHeight / 4, { align: 'center' });
// Subtitle
doc.setFontSize(16);
doc.setFont(undefined, 'normal');
doc.text('Generated with Angular 21', pageWidth / 2, pageHeight / 3, { align: 'center' });
// Date
doc.setTextColor(0, 0, 0);
doc.setFontSize(12);
const date = new Date().toLocaleDateString();
doc.text(`Generated: ${date}`, pageWidth / 2, pageHeight - 30, { align: 'center' });
this.addPageNumber(doc, pageNumber);
}
private addContentPage(
doc: jsPDF,
section: { heading: string; content: string[] },
pageNumber: number
): void {
let y = 30;
// Section heading
doc.setFontSize(18);
doc.setFont(undefined, 'bold');
doc.setTextColor(221, 0, 49);
doc.text(section.heading, 20, y);
// Underline
y += 2;
doc.setDrawColor(221, 0, 49);
doc.line(20, y, 190, y);
// Content
y += 15;
doc.setTextColor(0, 0, 0);
doc.setFontSize(12);
doc.setFont(undefined, 'normal');
section.content.forEach(paragraph => {
const lines = doc.splitTextToSize(paragraph, 170);
doc.text(lines, 20, y);
y += lines.length * 7 + 5;
});
this.addPageNumber(doc, pageNumber);
}
private addPageNumber(doc: jsPDF, pageNumber: number): void {
const pageHeight = doc.internal.pageSize.height;
doc.setFontSize(10);
doc.setTextColor(150, 150, 150);
doc.text(`Page ${pageNumber}`, 105, pageHeight - 10, { align: 'center' });
}
}
Table Generator
Create PDF documents with tables:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import jsPDF from 'jspdf';
interface TableRow {
id: number;
name: string;
email: string;
status: string;
}
@Component({
selector: 'app-table-pdf',
standalone: true,
imports: [CommonModule],
template: `
<div class="container">
<h2>User Data Table</h2>
<table class="data-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@for (row of tableData; track row.id) {
<tr>
<td>{{ row.id }}</td>
<td>{{ row.name }}</td>
<td>{{ row.email }}</td>
<td>{{ row.status }}</td>
</tr>
}
</tbody>
</table>
<button (click)="exportTableToPDF()" class="btn">
Export Table to PDF
</button>
</div>
`,
styles: [`
.container {
max-width: 900px;
margin: 0 auto;
padding: 20px;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
.data-table th,
.data-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
.data-table th {
background: #dd0031;
color: white;
font-weight: bold;
}
.data-table tr:hover {
background: #f5f5f5;
}
.btn {
background: #dd0031;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
}
`]
})
export class TablePdfComponent {
tableData: TableRow[] = [
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Active' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'Inactive' },
{ id: 4, name: 'Alice Brown', email: 'alice@example.com', status: 'Active' }
];
exportTableToPDF(): void {
const doc = new jsPDF();
// Title
doc.setFontSize(18);
doc.setFont(undefined, 'bold');
doc.text('User Data Report', 105, 20, { align: 'center' });
// Table headers
let y = 40;
const headers = ['ID', 'Name', 'Email', 'Status'];
const colWidths = [20, 50, 70, 30];
let x = 20;
doc.setFillColor(221, 0, 49);
doc.rect(20, y - 7, 170, 10, 'F');
doc.setTextColor(255, 255, 255);
doc.setFontSize(12);
doc.setFont(undefined, 'bold');
headers.forEach((header, i) => {
doc.text(header, x, y);
x += colWidths[i];
});
// Table rows
y += 10;
doc.setTextColor(0, 0, 0);
doc.setFont(undefined, 'normal');
doc.setFontSize(11);
this.tableData.forEach((row, index) => {
x = 20;
// Alternating row colors
if (index % 2 === 0) {
doc.setFillColor(245, 245, 245);
doc.rect(20, y - 6, 170, 8, 'F');
}
doc.text(row.id.toString(), x, y);
x += colWidths[0];
doc.text(row.name, x, y);
x += colWidths[1];
doc.text(row.email, x, y);
x += colWidths[2];
doc.text(row.status, x, y);
y += 10;
});
doc.save('user-data.pdf');
}
}
Adding Images
Handle image uploads and add them to PDFs:
import { Component, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import jsPDF from 'jspdf';
@Component({
selector: 'app-image-pdf',
standalone: true,
imports: [CommonModule],
template: `
<div class="container">
<h2>Add Image to PDF</h2>
<input
type="file"
(change)="handleFileUpload($event)"
accept="image/*"
class="file-input"
/>
@if (imagePreview()) {
<div class="preview">
<img [src]="imagePreview()" alt="Preview" />
</div>
}
<button
(click)="generatePDFWithImage()"
[disabled]="!imageData()"
class="btn"
>
Generate PDF with Image
</button>
</div>
`,
styles: [`
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.file-input {
margin: 20px 0;
}
.preview {
margin: 20px 0;
text-align: center;
}
.preview img {
max-width: 100%;
max-height: 300px;
border: 2px solid #ddd;
border-radius: 8px;
}
.btn {
width: 100%;
background: #dd0031;
color: white;
padding: 12px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn:disabled {
background: #ccc;
cursor: not-allowed;
}
`]
})
export class ImagePdfComponent {
imageData = signal<string | null>(null);
imagePreview = signal<string | null>(null);
handleFileUpload(event: Event): void {
const input = event.target as HTMLInputElement;
const file = input.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
const result = e.target?.result as string;
this.imageData.set(result);
this.imagePreview.set(result);
};
reader.readAsDataURL(file);
}
}
generatePDFWithImage(): void {
const data = this.imageData();
if (!data) return;
const doc = new jsPDF();
doc.setFontSize(18);
doc.setFont(undefined, 'bold');
doc.text('Document with Image', 105, 20, { align: 'center' });
doc.setFontSize(12);
doc.setFont(undefined, 'normal');
doc.text('Image embedded in PDF:', 20, 35);
doc.addImage(data, 'PNG', 15, 45, 180, 120);
doc.save('with-image.pdf');
}
}
Best Practices for Angular 21
- Use Standalone Components: Leverage Angular 21’s standalone architecture
- Signals for Reactivity: Use signals for reactive state management
- Inject Function: Use
inject()for cleaner dependency injection - Control Flow Syntax: Use
@if,@forfor better template syntax - Type Safety: Leverage TypeScript for type-safe PDF operations
- Service Layer: Create reusable PDF services with
providedIn: 'root' - Async Operations: Use
async/awaitfor HTML to PDF conversions - Error Handling: Implement proper error handling and user feedback
- Loading States: Show loading indicators during PDF generation
- Form Validation: Use Angular’s reactive forms for input validation
Common jsPDF Methods
new jsPDF()- Create new documentdoc.text(text, x, y)- Add textdoc.setFontSize(size)- Set font sizedoc.setFont(font, style)- Set font styledoc.setTextColor(r, g, b)- Set text colordoc.addPage()- Add new pagedoc.addImage(data, format, x, y, w, h)- Add imagedoc.rect(x, y, w, h, style)- Draw rectangledoc.line(x1, y1, x2, y2)- Draw linedoc.save(filename)- Download PDF
Performance Tips
- Generate PDFs only on user action
- Show loading states for large documents
- Optimize images before adding to PDFs
- Use OnPush change detection strategy
- Implement lazy loading for PDF components
- Cache reusable PDF templates
- Use Web Workers for heavy PDF operations
Browser Compatibility
jsPDF works in all modern browsers with Angular 21:
- Chrome 60+
- Firefox 55+
- Safari 11+
- Edge 79+
You now have comprehensive knowledge to create professional PDF documents in Angular 21 applications using jsPDF!
Related Articles
Generate and Scan QR Codes in Angular
Quick guide to generating and scanning QR codes in Angular browser applications. Simple examples with ngx-qrcode and html5-qrcode.
How to integrate jsPDF Library in React to Edit PDF in Browser
Quick guide to using jsPDF in React applications for creating and editing PDF documents directly in the browser.
jsPDF Tutorial: Generate PDF in Browser Using HTML & JavaScript (Full Working Example)
Learn to create PDFs directly in the browser with jsPDF. Step-by-step guide with working examples for invoices, tickets, and styled documents.