Created
October 22, 2024 06:29
-
-
Save Foadsf/6e7db7504b7a90a44fef4f23fe36765d to your computer and use it in GitHub Desktop.
Node.js script to convert SVGs to PDFs using Puppeteer, with support for Mermaid CLI's foreignObject elements and robust dimension handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const puppeteer = require('puppeteer'); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| async function convertSVGtoPDF(inputPath, outputPath) { | |
| // Validate input file exists and is SVG | |
| if (!fs.existsSync(inputPath)) { | |
| throw new Error('Input file does not exist'); | |
| } | |
| if (path.extname(inputPath).toLowerCase() !== '.svg') { | |
| throw new Error('Input file must be an SVG'); | |
| } | |
| // Read SVG content | |
| const svgContent = fs.readFileSync(inputPath, 'utf8'); | |
| // Launch browser | |
| const browser = await puppeteer.launch(); | |
| const page = await browser.newPage(); | |
| // Create HTML with SVG content and add viewBox if not present | |
| await page.setContent(` | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <style> | |
| body { margin: 0; } | |
| svg { | |
| width: 100%; | |
| height: 100%; | |
| max-width: 16384px; /* Chrome's max dimension */ | |
| max-height: 16384px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| ${svgContent} | |
| </body> | |
| </html> | |
| `); | |
| // Get and validate SVG dimensions | |
| const dimensions = await page.evaluate(() => { | |
| const svg = document.querySelector('svg'); | |
| let width = svg.width.baseVal.value; | |
| let height = svg.height.baseVal.value; | |
| // If dimensions are 0 or not set, try to get them from viewBox | |
| if (!width || !height) { | |
| const viewBox = svg.viewBox.baseVal; | |
| if (viewBox) { | |
| width = viewBox.width; | |
| height = viewBox.height; | |
| } | |
| } | |
| // If still no dimensions, try to get from bounding box | |
| if (!width || !height) { | |
| const bbox = svg.getBBox(); | |
| width = bbox.width; | |
| height = bbox.height; | |
| } | |
| // Ensure dimensions are valid numbers and within reasonable limits | |
| width = Math.min(Math.max(width, 1), 14400); // Max 14400 to be safe | |
| height = Math.min(Math.max(height, 1), 14400); | |
| return { width, height }; | |
| }); | |
| // Apply a scale factor if dimensions are too large | |
| const maxDimension = 14400; // Maximum safe dimension | |
| let scale = 1; | |
| if (dimensions.width > maxDimension || dimensions.height > maxDimension) { | |
| scale = Math.min( | |
| maxDimension / dimensions.width, | |
| maxDimension / dimensions.height | |
| ); | |
| } | |
| const finalDimensions = { | |
| width: Math.round(dimensions.width * scale), | |
| height: Math.round(dimensions.height * scale) | |
| }; | |
| // Log dimensions for debugging | |
| console.log('Original dimensions:', dimensions); | |
| console.log('Final dimensions:', finalDimensions); | |
| console.log('Scale factor:', scale); | |
| try { | |
| // Set viewport | |
| await page.setViewport({ | |
| width: finalDimensions.width, | |
| height: finalDimensions.height | |
| }); | |
| // Generate PDF | |
| await page.pdf({ | |
| path: outputPath, | |
| width: finalDimensions.width, | |
| height: finalDimensions.height, | |
| printBackground: true | |
| }); | |
| } catch (error) { | |
| console.error('Detailed error:', error); | |
| // Fallback to default dimensions if still getting errors | |
| const fallbackDimensions = { | |
| width: 800, | |
| height: 600 | |
| }; | |
| console.log('Falling back to default dimensions:', fallbackDimensions); | |
| await page.setViewport(fallbackDimensions); | |
| await page.pdf({ | |
| path: outputPath, | |
| ...fallbackDimensions, | |
| printBackground: true | |
| }); | |
| } | |
| await browser.close(); | |
| } | |
| // Command line interface | |
| if (require.main === module) { | |
| const args = process.argv.slice(2); | |
| if (args.length !== 2) { | |
| console.log('Usage: node script.js <input.svg> <output.pdf>'); | |
| process.exit(1); | |
| } | |
| const [inputPath, outputPath] = args; | |
| convertSVGtoPDF(inputPath, outputPath) | |
| .then(() => { | |
| console.log(`Successfully converted ${inputPath} to ${outputPath}`); | |
| }) | |
| .catch(error => { | |
| console.error('Error:', error.message); | |
| process.exit(1); | |
| }); | |
| } | |
| module.exports = convertSVGtoPDF; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment