safeer-pdf-generator
Version:
Framework-agnostic PDF generation library with chunking, merging, S3 upload, and email delivery
143 lines • 5.18 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertImageToBase64 = convertImageToBase64;
exports.isImageUrl = isImageUrl;
exports.getImageDimensions = getImageDimensions;
exports.calculateResizedDimensions = calculateResizedDimensions;
exports.getImageExtension = getImageExtension;
exports.generatePlaceholderImage = generatePlaceholderImage;
const defaults_js_1 = require("../config/defaults.js");
/**
* Convert image URL to base64 data URI
*/
async function convertImageToBase64(url, logger = defaults_js_1.noOpLogger, fetcher) {
if (!url) {
logger.debug('Empty URL provided for image conversion');
return '';
}
try {
// Handle data URIs directly
if (url.startsWith('data:image')) {
logger.debug('Image is already a data URI');
return url;
}
// Use custom fetcher if provided
if (fetcher) {
logger.debug('Using custom fetcher for image conversion');
return await fetcher(url);
}
// Handle external URLs with fetch
logger.debug(`Fetching image from URL: ${url}`);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const buffer = await response.arrayBuffer();
const contentType = response.headers.get('content-type') || 'application/octet-stream';
// Validate content type
if (!contentType.startsWith('image/')) {
logger.warn(`Invalid content type for image: ${contentType}`);
return '';
}
const base64 = Buffer.from(buffer).toString('base64');
const dataUri = `data:${contentType};base64,${base64}`;
logger.debug(`Successfully converted image to base64 (${(base64.length / 1024).toFixed(1)}KB)`);
return dataUri;
}
catch (error) {
logger.error(`Image conversion failed for URL ${url}:`, error);
return ''; // Return empty string to avoid breaking template
}
}
/**
* Validate if a URL looks like an image
*/
function isImageUrl(url) {
if (!url || typeof url !== 'string')
return false;
// Data URI check
if (url.startsWith('data:image/'))
return true;
// File extension check
const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|svg|webp)(\?.*)?$/i;
return imageExtensions.test(url);
}
/**
* Get image dimensions from buffer (basic PNG/JPEG support)
*/
function getImageDimensions(buffer) {
try {
// PNG signature
if (buffer.length >= 24 && buffer.toString('hex', 0, 8) === '89504e470d0a1a0a') {
const width = buffer.readUInt32BE(16);
const height = buffer.readUInt32BE(20);
return { width, height };
}
// JPEG signature
if (buffer.length >= 4 && buffer[0] === 0xff && buffer[1] === 0xd8) {
// This is a simplified JPEG parser - for production, consider using a proper image library
let offset = 2;
while (offset < buffer.length - 8) {
if (buffer[offset] === 0xff &&
(buffer[offset + 1] === 0xc0 || buffer[offset + 1] === 0xc2)) {
const height = buffer.readUInt16BE(offset + 5);
const width = buffer.readUInt16BE(offset + 7);
return { width, height };
}
offset++;
}
}
return null;
}
catch (error) {
return null;
}
}
/**
* Resize image dimensions while maintaining aspect ratio
*/
function calculateResizedDimensions(originalWidth, originalHeight, maxWidth, maxHeight) {
const aspectRatio = originalWidth / originalHeight;
let width = originalWidth;
let height = originalHeight;
// Scale down if too wide
if (width > maxWidth) {
width = maxWidth;
height = width / aspectRatio;
}
// Scale down if too tall
if (height > maxHeight) {
height = maxHeight;
width = height * aspectRatio;
}
return {
width: Math.round(width),
height: Math.round(height),
};
}
/**
* Extract file extension from URL or filename
*/
function getImageExtension(url) {
if (url.startsWith('data:image/')) {
const match = url.match(/data:image\/([^;]+)/);
return match ? match[1] : '';
}
const match = url.match(/\.([a-zA-Z0-9]+)(\?.*)?$/);
return match ? match[1].toLowerCase() : '';
}
/**
* Generate a fallback image placeholder as data URI
*/
function generatePlaceholderImage(width = 100, height = 100, text = '?') {
// Simple SVG placeholder
const svg = `
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#f0f0f0" stroke="#ddd" stroke-width="1"/>
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle"
font-family="Arial, sans-serif" font-size="14" fill="#999">${text}</text>
</svg>
`.trim();
return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
}
//# sourceMappingURL=image.js.map