Please bookmark this page to avoid losing your image tool!

Image To PDF Converter

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
async function processImage(originalImg, filename = "converted.pdf", pageSize = "a4", orientation = "portrait", pageMargin = 10) {
    // Helper to create consistent error messages
    const createErrorElement = (message) => {
        console.error(message);
        const pError = document.createElement('p');
        pError.textContent = message;
        pError.style.color = 'red';
        pError.style.padding = '10px';
        pError.style.border = '1px solid red';
        pError.style.backgroundColor = '#ffeeee';
        return pError;
    };

    // 1. Validate image and wait for load if necessary
    if (!originalImg || !(originalImg instanceof HTMLImageElement)) {
        return createErrorElement("Error: Input is not a valid HTMLImageElement.");
    }

    if (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        if (originalImg.src && !originalImg.complete) { // Image has src, but not yet loaded
            console.warn("Image not fully loaded, attempting to wait for 2 seconds...");
            try {
                await new Promise((resolve, reject) => {
                    const oldOnload = originalImg.onload;
                    const oldOnerror = originalImg.onerror;
                    let timeoutId = null;

                    originalImg.onload = (e) => {
                        clearTimeout(timeoutId);
                        if (oldOnload) oldOnload.call(originalImg, e);
                        resolve();
                    };
                    originalImg.onerror = (e) => {
                        clearTimeout(timeoutId);
                        if (oldOnerror) oldOnerror.call(originalImg, e);
                        reject(new Error("Image failed to load during explicit wait."));
                    };
                    
                    timeoutId = setTimeout(() => {
                        // Restore original handlers to prevent memory leaks if image loads much later
                        originalImg.onload = oldOnload;
                        originalImg.onerror = oldOnerror;
                        reject(new Error("Image loading timed out (2s) during explicit wait."));
                    }, 2000);
                });

                if (originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
                    throw new Error("Image loaded successfully but its dimensions are zero (possibly a broken image file).");
                }
                console.log("Image successfully loaded after explicit wait.");
            } catch (e) {
                return createErrorElement(`Error: Input image could not be loaded. Details: ${e.message}`);
            }
        } else { // No src, or already complete but dimensions are 0 (e.g. broken image)
            const reason = originalImg.complete ? "Image is loaded but dimensions are zero - possibly broken." : "Image has no source or is not loaded.";
            return createErrorElement(`Error: Input image is invalid. ${reason}`);
        }
    }

    // 2. Load jsPDF library
    if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') {
        try {
            await new Promise((resolve, reject) => {
                const script = document.createElement('script');
                script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
                script.async = true;
                script.onload = () => {
                    if (typeof window.jspdf !== 'undefined' && typeof window.jspdf.jsPDF !== 'undefined') {
                        resolve();
                    } else {
                        reject(new Error('jsPDF library loaded but window.jspdf.jsPDF is not defined.'));
                    }
                };
                script.onerror = () => reject(new Error('Failed to load jsPDF script from CDN.'));
                document.head.appendChild(script);
            });
        } catch (error) {
            return createErrorElement(`Error: Required PDF library (jsPDF) could not be loaded. ${error.message}`);
        }
    }
    const { jsPDF } = window.jspdf;

    // 3. Prepare image data for PDF
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d');
    tempCanvas.width = originalImg.naturalWidth;
    tempCanvas.height = originalImg.naturalHeight;
    
    try {
        tempCtx.drawImage(originalImg, 0, 0, originalImg.naturalWidth, originalImg.naturalHeight);
    } catch (e) {
        return createErrorElement(`Error: Could not draw image to canvas. It might be tainted (CORS) or an unsupported format. ${e.message}`);
    }

    let imageDataUrl;
    let imageTypeForJsPdf;
    try {
        // JPEG is generally good for photos and offers compression
        imageDataUrl = tempCanvas.toDataURL('image/jpeg', 0.9); // 0.9 quality
        imageTypeForJsPdf = 'JPEG';
    } catch (e) {
        console.warn("toDataURL with image/jpeg failed, falling back to image/png:", e);
        try {
            imageDataUrl = tempCanvas.toDataURL('image/png');
            imageTypeForJsPdf = 'PNG';
        } catch (e2) {
            return createErrorElement(`Error: Could not get image data from canvas (tried JPEG and PNG). ${e2.message}`);
        }
    }

    if (!imageDataUrl || imageDataUrl === "data:,") {
        return createErrorElement("Error: Extracted image data is empty or invalid.");
    }

    // 4. Sanitize parameters
    let outputFilename = String(filename).trim();
    if (!outputFilename.toLowerCase().endsWith('.pdf')) {
        outputFilename += '.pdf';
    }
    if (outputFilename === ".pdf" || outputFilename.length === 0) { // If filename was empty or just spaces then ".pdf"
        outputFilename = "converted.pdf";
    }

    const unit = "mm"; // Fixed unit for consistent margin interpretation
    
    let currentMargin = Number(pageMargin);
    if (isNaN(currentMargin) || currentMargin < 0) {
        console.warn(`Invalid pageMargin value "${pageMargin}", defaulting to 0.`);
        currentMargin = 0;
    }

    let jsPdfOrientation = 'p'; // default to portrait
    const oLower = String(orientation).toLowerCase();
    if (oLower === 'l' || oLower === 'landscape') {
        jsPdfOrientation = 'l';
    }

    const finalPageSize = String(pageSize).toLowerCase();

    // 5. Create PDF document
    const doc = new jsPDF({
        orientation: jsPdfOrientation,
        unit: unit,
        format: finalPageSize,
        putOnlyUsedFonts: true,
        compress: true 
    });

    // 6. Calculate image placement
    const pageFormat = doc.internal.pageSize;
    const pageWidth = pageFormat.getWidth();
    const pageHeight = pageFormat.getHeight();
    
    const availableWidth = pageWidth - 2 * currentMargin;
    const availableHeight = pageHeight - 2 * currentMargin;

    if (availableWidth <= 0 || availableHeight <= 0) {
        return createErrorElement(`Error: Margins (${currentMargin}${unit}) are too large for page size "${finalPageSize}". Available drawing area is zero or negative.`);
    }

    const imgWidth = originalImg.naturalWidth;
    const imgHeight = originalImg.naturalHeight;
    const aspectRatio = imgWidth / imgHeight;

    let pdfImgFinalWidth = imgWidth;
    let pdfImgFinalHeight = imgHeight;

    // Scale image down if it's too large for the available space, maintaining aspect ratio
    if (pdfImgFinalWidth > availableWidth) {
        pdfImgFinalWidth = availableWidth;
        pdfImgFinalHeight = pdfImgFinalWidth / aspectRatio;
    }
    // After adjusting for width, check if height is now an issue
    if (pdfImgFinalHeight > availableHeight) {
        pdfImgFinalHeight = availableHeight;
        pdfImgFinalWidth = pdfImgFinalHeight * aspectRatio;
    }
    
    // Center the image on the page
    const x = currentMargin + (availableWidth - pdfImgFinalWidth) / 2;
    const y = currentMargin + (availableHeight - pdfImgFinalHeight) / 2;

    // 7. Add image to PDF
    try {
        doc.addImage(imageDataUrl, imageTypeForJsPdf, x, y, pdfImgFinalWidth, pdfImgFinalHeight);
    } catch (e) {
        let message = `Error: Failed to add image to PDF. ${e.message}`;
        if (e.message && e.message.includes("Unsupported format")) {
            message = `Error: The image format "${imageTypeForJsPdf}" might be unsupported or there was an issue with image data. Try another image.`;
        }
        return createErrorElement(message);
    }
    
    // 8. Create download link
    let pdfOutputURI;
    try {
         // Using bloburl for better performance with large PDFs compared to datauristring
         pdfOutputURI = doc.output('bloburl'); 
    } catch (e) {
        console.warn("Error generating PDF output with bloburl, trying datauristring:", e);
        try {
            pdfOutputURI = doc.output('datauristring');
        } catch (e2) {
           return createErrorElement(`Error: Failed to generate PDF output. ${e2.message}`);
        }
    }

    const link = document.createElement('a');
    link.href = pdfOutputURI;
    link.download = outputFilename;
    link.textContent = `Download ${outputFilename}`;
    
    // Basic styling for the link
    link.style.display = 'inline-block';
    link.style.padding = '10px 15px';
    link.style.backgroundColor = '#007bff';
    link.style.color = 'white';
    link.style.textDecoration = 'none';
    link.style.borderRadius = '5px';
    link.style.border = 'none';
    link.style.cursor = 'pointer';
    link.target = '_blank'; // Open blob URLs in a new tab if not downloading directly seems to be more common.

    // Add hover effect using JS as it survives .html() better if only style is set
    // Setting styles directly is robust for .html() insertion.
    // For actual event listeners like mouseover in JS, it's tricky with .html().
    // To keep it simple and robust given the .html() constraint, rely on static styles.
    // A simple :hover can be defined in CSS by the host page.

    return link;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image To PDF Converter is a web tool that allows users to convert images into PDF format. This utility supports various image types and lets users customize the PDF output, including page size, orientation, and margins. It is ideal for creating documents from images, such as compiling photos into a photo album, converting receipts or invoices into a single PDF for better organization, and sharing image-based content in a universally accepted format. With easy-to-use functionality, users can generate a downloadable PDF that retains the quality and aspect ratio of the original images.

Leave a Reply

Your email address will not be published. Required fields are marked *