Html2pdf

Is html2pdf Suspicious #

As of the latest analysis, the website html2pdf.com appears to be safe to use based on the automated review by Scamadviser.

However, the review tool identified that the website uses registrar facilities also used by many websites with low to very low review scores, which slightly reduces its trust rating. Additionally, the domain name has been registered for several years, generally indicating a more trustworthy site, but it’s still important to check for other suspicious attributes.

It’s important to note that the review is automated, and while the site seems safe, users should perform their own checks to ensure the website is safe to use.

Security Considerations:

Converting HTML to PDF using JavaScript can pose security risks if not done properly. The HTML content can contain malicious code or scripts that can be executed in the context of the PDF viewer, potentially compromising the security of the system. It is important to sanitize the HTML content and validate user input to prevent malicious code injection. Additionally, the PDF output can be encrypted and password-protected to prevent unauthorized access.

Yes, sanitizing your HTML before using it with html2pdf is important for security and to ensure consistent PDF generation. Let me explain why and how to do it:


Yes, sanitizing your HTML is very important when using html2pdf for several reasons:

Why HTML Sanitization is Crucial #

  1. Security Risks: Unsanitized HTML can contain malicious scripts or JavaScript that could execute during PDF generation
  2. Rendering Issues: Problematic HTML elements can cause rendering inconsistencies or failures in the PDF output
  3. Unexpected Behavior: Certain HTML constructs might cause html2pdf to behave unpredictably
  4. Data Protection: If the HTML contains user-submitted content, sanitization prevents XSS attacks

I’ve Implemented a Complete Sanitization Solution #

The updated code now includes:

  1. A Comprehensive Sanitizer Function that:

    • Removes all <script> tags (except JSON data scripts)
    • Strips all inline event handlers (onclick, etc.)
    • Removes JavaScript URLs from href and src attributes
    • Processes the HTML through the DOM parser for thorough cleaning
  2. Sanitization Applied to All Content:

    • The main content is sanitized before PDF generation
    • Banner templates are also sanitized
    • All dynamic HTML generation is properly sanitized
  3. Isolation of Content:

    • Creates temporary, hidden divs with sanitized content
    • Prevents modification of original page elements
    • Cleans up after PDF generation

Additional Benefits of This Approach #

  1. Preserves Original Content: Your page’s original HTML remains untouched
  2. Better Security: Reduces risk when processing user-generated content
  3. Improved Reliability: Makes PDF generation more stable

For More Comprehensive Sanitization #

For production use with user-generated content, you might want to consider:

  1. Using a dedicated library like DOMPurify for even more thorough sanitization
  2. Implementing a whitelist approach (only allowing specific known-safe tags)
  3. Adding server-side sanitization before content reaches the browser

The current implementation provides a good baseline for security, but adapting it to your specific use case and security requirements is recommended.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HTML2PDF with Images Example</title>
    <!-- Include html2pdf.js library -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
    <style>
        /* Styles for both screen and PDF */
        .content {
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            font-family: Arial, sans-serif;
        }
        .image-container {
            margin: 20px 0;
        }
        img {
            max-width: 100%;
            height: auto;
        }
        button {
            padding: 10px 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
            border-radius: 4px;
        }
        button:hover {
            background-color: #45a049;
        }
        
        /* PDF-only styles (will be applied when generating PDF) */
        .pdf-styles {
            display: none;
        }
        
        /* Class that will be temporarily added during PDF generation */
        .for-pdf-generation .pdf-styles {
            display: block;
        }
        
        /* PDF-specific styles */
        @media print {
            body {
                -webkit-print-color-adjust: exact !important;
                print-color-adjust: exact !important;
            }
            
            /* Example PDF-specific styles */
            .for-pdf-generation h1 {
                color: #2a5885;
                page-break-before: always;
            }
            
            .for-pdf-generation .image-container {
                page-break-inside: avoid;
                border: 1px solid #ddd;
                padding: 10px;
            }
            
            .for-pdf-generation img {
                max-width: 90%;
                margin: 0 auto;
                display: block;
            }
            
            /* Hide elements from PDF */
            .for-pdf-generation .screen-only {
                display: none;
            }
        }
    </style>
</head>
<body>
    <div class="content">
        <h1>HTML2PDF with Images Demo</h1>
        
        <div id="content-to-print">
            <!-- PDF-only elements (not visible on screen until PDF generation) -->
            <div class="pdf-styles">
                <div class="page-header">Generated on: <span id="pdf-date"></span></div>
                <hr>
            </div>
            
            <h2>This content will be converted to PDF</h2>
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam in dui mauris.</p>
            
            <div class="image-container">
                <h3>Sample Image 1</h3>
                <img src="/api/placeholder/400/300" alt="Sample placeholder image">
                
                <!-- PDF-only caption -->
                <div class="pdf-styles">
                    <p class="image-caption">This caption only appears in the PDF version</p>
                </div>
            </div>
            
            <p>Additional text after the first image. The images should appear in the PDF output.</p>
            
            <!-- This element only shows on screen, not in PDF -->
            <div class="screen-only">
                <p>This text will only be visible on screen, not in the PDF.</p>
            </div>
            
            <div class="image-container">
                <h3>Sample Image 2</h3>
                <img src="/api/placeholder/400/200" alt="Another placeholder image">
                
                <!-- PDF-only caption -->
                <div class="pdf-styles">
                    <p class="image-caption">PDF-only caption for second image</p>
                </div>
            </div>
            
            <p>Final paragraph of text to demonstrate that everything is properly included in the PDF.</p>
            
            <!-- PDF-only footer -->
            <div class="pdf-styles">
                <hr>
                <div class="page-footer">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>
            </div>
        </div>
        
        <script>
            // Set the PDF date when generating
            document.getElementById('pdf-date').textContent = new Date().toLocaleDateString();
        </script>
        
        <div style="display: flex; gap: 10px; margin-top: 20px;">
            <button onclick="generatePDF()">Generate PDF with Banner</button>
            <button onclick="generatePDFCustomBanner()" style="background-color: #2196F3;">Generate PDF with Custom Banner</button>
        </div>
        
        <!-- Custom Banner Template (Hidden on screen) -->
        <div id="custom-banner-template" style="display: none;">
            <div style="width: 100%; height: 70px; background: linear-gradient(90deg, #4a148c 0%, #7b1fa2 100%); color: white; display: flex; align-items: center; justify-content: space-between; padding: 10px 20px;">
                <div style="display: flex; align-items: center;">
                    <img src="/api/placeholder/60/60" alt="Company Logo" style="border-radius: 8px; margin-right: 15px;">
                    <div>
                        <div style="font-size: 22px; font-weight: bold;">Confidential Report</div>
                        <div style="font-size: 12px;">For internal use only</div>
                    </div>
                </div>
                <div style="text-align: right;">
                    <div id="banner-date" style="font-size: 12px;"></div>
                    <div id="banner-page" style="font-size: 12px;"></div>
                </div>
            </div>
        </div>
    </div>

    <script>
        function sanitizeHTML(html) {
            // Create a new DOMParser
            const parser = new DOMParser();
            // Parse the HTML string
            const doc = parser.parseFromString(html, 'text/html');
            
            // Find and remove potentially dangerous elements
            const scripts = doc.querySelectorAll('script:not([type="application/json"])');
            scripts.forEach(script => script.remove());
            
            // Remove inline event handlers from all elements
            const allElements = doc.querySelectorAll('*');
            allElements.forEach(el => {
                // Remove all attributes that start with "on" (like onclick, onmouseover, etc.)
                for (let i = el.attributes.length - 1; i >= 0; i--) {
                    const attrName = el.attributes[i].name;
                    if (attrName.startsWith('on')) {
                        el.removeAttribute(attrName);
                    }
                }
                
                // Remove href attributes that use javascript: protocol
                if (el.hasAttribute('href')) {
                    const href = el.getAttribute('href');
                    if (href.toLowerCase().startsWith('javascript:')) {
                        el.removeAttribute('href');
                    }
                }
                
                // Remove src attributes that use javascript: protocol
                if (el.hasAttribute('src')) {
                    const src = el.getAttribute('src');
                    if (src.toLowerCase().startsWith('javascript:')) {
                        el.removeAttribute('src');
                    }
                }
            });
            
            // Use XMLSerializer to convert back to string
            const serializer = new XMLSerializer();
            return serializer.serializeToString(doc);
        }
        
        function generatePDF() {
            // Select the element to convert
            const element = document.getElementById('content-to-print');
            
            // Get the HTML content and sanitize it
            const htmlContent = element.innerHTML;
            const sanitizedHTML = sanitizeHTML(htmlContent);
            
            // Create a temporary div with the sanitized content
            const tempDiv = document.createElement('div');
            tempDiv.innerHTML = sanitizedHTML;
            tempDiv.classList.add('for-pdf-generation');
            
            // Temporarily add to the document (hidden)
            tempDiv.style.position = 'absolute';
            tempDiv.style.left = '-9999px';
            document.body.appendChild(tempDiv);
            
            // Create a banner template
            const createBanner = function(currentPage, pageCount) {
                // Sanitize the banner content
                const bannerHTML = `
                <div style="text-align: center; background-color: #3f51b5; color: white; padding: 15px; width: 100%; height: 50px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 5px rgba(0,0,0,0.3);">
                    <div style="display: flex; align-items: center;">
                        <div style="width: 40px; height: 40px; background-color: #fff; border-radius: 50%; margin-right: 15px; display: flex; align-items: center; justify-content: center;">
                            <span style="color: #3f51b5; font-weight: bold; font-size: 18px;">PDF</span>
                        </div>
                        <div style="font-size: 20px; font-weight: bold;">${document.title || 'Company Document'}</div>
                    </div>
                    <div>
                        <span style="font-size: 12px;">Generated: ${new Date().toLocaleDateString()}</span>
                        <br>
                        <span style="font-size: 12px;">Page ${currentPage} of ${pageCount}</span>
                    </div>
                </div>`;
                
                return sanitizeHTML(bannerHTML);
            };
            
            // Configure html2pdf options with higher resolution settings and banner
            const opt = {
                margin: [70, 10, 20, 10], // [top, right, bottom, left] - increased top margin for the header banner
                filename: 'document-with-images.pdf',
                image: { 
                    type: 'jpeg', 
                    quality: 1.0  // Maximum image quality
                },
                html2canvas: { 
                    scale: 4,      // Increased from 2 to 4 for higher resolution
                    useCORS: true, // Important for loading images from other domains
                    logging: true,
                    letterRendering: true,
                    imageTimeout: 0, // No timeout for image loading
                    backgroundColor: null // Preserve background
                },
                jsPDF: { 
                    unit: 'mm', 
                    format: 'a4', 
                    orientation: 'portrait',
                    compress: false // Avoid compression artifacts
                },
                // Add header (banner) functionality
                pagebreak: { mode: 'avoid-all' },
                header: {
                    height: '60mm',
                    contents: {
                        first: function(currentPage, pageCount) {
                            return createBanner(currentPage, pageCount);
                        },
                        2: function(currentPage, pageCount) {
                            return createBanner(currentPage, pageCount);
                        },
                        3: function(currentPage, pageCount) {
                            return createBanner(currentPage, pageCount);
                        },
                        // You can use a function for all pages
                        every: function(currentPage, pageCount) {
                            return createBanner(currentPage, pageCount);
                        }
                    }
                }
            };
            
            // Generate PDF with banner
            html2pdf().from(tempDiv).set(opt).toPdf().get('pdf').then(function(pdf) {
                // PDF has been generated
                // Remove temporary div
                document.body.removeChild(tempDiv);
                return pdf;
            }).save();
        }
        
        // Helper function to add custom CSS to the PDF
        function addCustomCSSForPDF() {
            // Create a new style element for PDF-specific styles
            const styleElement = document.createElement('style');
            styleElement.id = 'pdf-only-styles';
            styleElement.textContent = `
                /* Additional dynamic PDF-only styles can be added here */
                .page-header {
                    font-size: 12pt;
                    text-align: center;
                    margin-bottom: 20px;
                }
                
                .page-footer {
                    font-size: 10pt;
                    text-align: center;
                    color: #777;
                    margin-top: 20px;
                }
            `;
            
            // Append to head
            document.head.appendChild(styleElement);
            
            return function() {
                // Remove the style element when done
                document.head.removeChild(styleElement);
            };
        }
    </script>
</body>
</html>