Please bookmark this page to avoid losing your image tool!

Image Romance Novel Cover Template Creator

(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,
    titleText = "Whispers of Desire",
    authorText = "By A. N. Romancer",
    taglineText = "A love that defied all odds.", // Set to "" or null to hide tagline
    titleFontFamily = "Great Vibes", // A romantic script font. Ensure URL points to its woff2 file.
    titleFontUrl = "https://fonts.gstatic.com/s/greatvibes/v18/RWmMoKWR9v4hfMFdnkLqCohenjI.woff2",
    authorFontFamily = "Cinzel", // An elegant serif font. Ensure URL points to its woff2 file.
    authorFontUrl = "https://fonts.gstatic.com/s/cinzel/v20/8vIJ7ww63mVu7gt7FfgrkFcHoA.woff2",
    taglineFontFamily = "Roboto Condensed", // A clean, readable sans-serif.
    taglineFontUrl = "https://fonts.gstatic.com/s/robotocondensed/v27/ieVo2ZhZI2eCN5iU1GcYvgEigLs8Bqi4An-_rA.woff2",
    titleColor = "#FFD700", // Gold color for richness
    authorColor = "#FFFFFF", // Classic white
    taglineColor = "#F0F0F0", // Slightly off-white
    shadowColor = "rgba(0,0,0,0.7)",
    shadowBlur = 7,
    shadowOffsetX = 2,
    shadowOffsetY = 2,
    overlayColor = "rgba(50,20,70,0.25)", // Dark moody purple overlay; "transparent" or "" for none
    vignetteStrength = 0.7, // Range 0 (none) to 1 (strong). Affects darkness and spread.
    targetCoverWidth = 800, // Output canvas width in pixels
    targetCoverAspectRatio = 1.6 // Height / Width (e.g., 1.6 for a 6x9.6 inch equivalent)
) {

    // Validate originalImg
    if (!originalImg || typeof originalImg.naturalWidth === 'undefined' || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        console.error("Original image is not loaded or invalid. Ensure originalImg is a loaded HTMLImageElement.");
        const errorCanvas = document.createElement('canvas');
        errorCanvas.width = targetCoverWidth;
        errorCanvas.height = targetCoverWidth * targetCoverAspectRatio;
        const errCtx = errorCanvas.getContext('2d');
        errCtx.fillStyle = 'lightgray';
        errCtx.fillRect(0,0, errorCanvas.width, errorCanvas.height);
        errCtx.fillStyle = 'red';
        errCtx.font = `${Math.round(targetCoverWidth*0.03)}px Arial`;
        errCtx.textAlign = 'center';
        errCtx.textBaseline = 'middle';
        errCtx.fillText("Error: Image not loaded or invalid.", errorCanvas.width/2, errorCanvas.height/2);
        return errorCanvas;
    }

    // Initialize font loading cache on the function itself if not already present
    // This cache persists across multiple calls to processImage
    if (!processImage.fontLoadPromises) {
        processImage.fontLoadPromises = {};
    }

    // Helper function to load web fonts (nested to be part of the main function block)
    async function loadWebFont_internal(fontFamily, fontUrl) {
        if (!fontFamily || !fontUrl) return false; // No font/URL specified, treated as no-op for this font

        if (document.fonts) { // Check for Font Loading API support
            // Check if font is already system-available or globally loaded and tracked by document.fonts
             try { // Enclose document.fonts access in try-catch for stricter environments (e.g. sandboxed iframes)
                if (document.fonts.check(`1em "${fontFamily}"`)) {
                     // console.log(`Font "${fontFamily}" is already available (system or CSS-loaded).`);
                    return true;
                }
            } catch(e) {
                // console.warn("Could not check document.fonts:", e);
            }


            // Check our own loading cache
            if (processImage.fontLoadPromises[fontFamily]) {
                // console.log(`Waiting for existing load promise of "${fontFamily}".`);
                return processImage.fontLoadPromises[fontFamily]; // Return existing promise
            }

            // Create and cache the font loading promise
            // console.log(`Attempting to load font "${fontFamily}" from ${fontUrl}`);
            const fontFace = new FontFace(fontFamily, `url(${fontUrl})`);
            processImage.fontLoadPromises[fontFamily] = fontFace.load().then(loadedFace => {
                document.fonts.add(loadedFace);
                // console.log(`Font "${fontFamily}" loaded successfully.`);
                return true;
            }).catch(e => {
                console.error(`Failed to load font '${fontFamily}' from ${fontUrl}:`, e);
                delete processImage.fontLoadPromises[fontFamily]; // Remove failed promise to allow retry
                return false; // Indicate failure
            });
            return processImage.fontLoadPromises[fontFamily];
        }
        
        // Fallback if document.fonts API is not supported
        console.warn("document.fonts API not supported by this browser. Fonts may not load as expected.");
        return false; // Indicate that font loading might not occur
    }

    // Text wrapping helper function (nested)
    function wrapText_internal(context, text, x, yCenter, maxWidth, lineHeight) {
        if (!text || text.trim() === "") return;

        const words = text.split(' ');
        let line = '';
        let lines = [];

        for (let n = 0; n < words.length; n++) {
            let testLine = line + words[n] + ' ';
            // The font must be set on the context BEFORE calling measureText
            let metrics = context.measureText(testLine);
            let testWidth = metrics.width;
            if (testWidth > maxWidth && n > 0) { // If the line exceeds max width (and it's not the first word)
                lines.push(line.trim()); // Add the current line (without the new word)
                line = words[n] + ' '; // Start a new line with the current word
            } else {
                line = testLine; // Otherwise, add the word to the current line
            }
        }
        lines.push(line.trim()); // Add the last line

        context.textBaseline = 'middle'; // Vertically align text nicely
        
        // Calculate the starting Y position to center the entire text block vertically at yCenter
        const totalTextBlockHeight = (lines.length - 1) * lineHeight;
        let currentY = yCenter - totalTextBlockHeight / 2;

        for (let i = 0; i < lines.length; i++) {
            context.fillText(lines[i], x, currentY);
            currentY += lineHeight; // Move to the next line position
        }
    }

    // Load all necessary fonts concurrently.
    // Using await Promise.all ensures all font loading attempts are made before drawing.
    // Individual failures are handled within loadWebFont_internal (logged, returns false).
    // The browser will use fallback fonts specified in ctx.font if custom fonts fail.
    await Promise.all([
        loadWebFont_internal(titleFontFamily, titleFontUrl),
        loadWebFont_internal(authorFontFamily, authorFontUrl),
        loadWebFont_internal(taglineFontFamily, taglineFontUrl)
    ]);

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Set canvas dimensions based on target width and aspect ratio
    canvas.width = targetCoverWidth;
    canvas.height = Math.round(targetCoverWidth * targetCoverAspectRatio);

    // --- Draw Background Image (cropped/scaled to fit target aspect ratio) ---
    const imgAspectRatio = originalImg.naturalHeight / originalImg.naturalWidth;
    let sx = 0, sy = 0, sWidth = originalImg.naturalWidth, sHeight = originalImg.naturalHeight;

    if (imgAspectRatio > targetCoverAspectRatio) { // Image is taller/slimmer than target
        sHeight = originalImg.naturalWidth * targetCoverAspectRatio; // Calculate new source height to match target AR
        sy = (originalImg.naturalHeight - sHeight) / 2; // Center crop vertically
    } else if (imgAspectRatio < targetCoverAspectRatio) { // Image is wider/shorter than target
        sWidth = originalImg.naturalHeight / targetCoverAspectRatio; // Calculate new source width
        sx = (originalImg.naturalWidth - sWidth) / 2; // Center crop horizontally
    }
    // Draw the (potentially cropped)_alignment source image_alignment onto the entire canvas
    ctx.drawImage(originalImg, sx, sy, sWidth, sHeight, 0, 0, canvas.width, canvas.height);

    // --- Apply Color Overlay ---
    if (overlayColor && overlayColor !== "transparent" && overlayColor !== "" && overlayColor !== "none") {
        ctx.fillStyle = overlayColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // --- Apply Vignette Effect ---
    if (vignetteStrength > 0) {
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        // Outer radius should extend slightly beyond canvas corners to ensure full coverage
        const outerRadius = Math.sqrt(centerX*centerX + centerY*centerY) * 1.1; 
        // Inner radius of the transparent part: shrinks as strength increases
        // The 0.7 and 0.65 factors control the gradient spread and central light area size
        const innerRadiusPercent = Math.max(0.05, 0.7 - vignetteStrength * 0.65); 
        const innerRadius = outerRadius * innerRadiusPercent;
        
        const gradient = ctx.createRadialGradient(centerX, centerY, innerRadius, centerX, centerY, outerRadius);
        gradient.addColorStop(0, 'rgba(0,0,0,0)'); // Center is transparent
        // Edge darkness increases with strength
        gradient.addColorStop(1, `rgba(0,0,0,${Math.min(1, vignetteStrength)})`);

        ctx.fillStyle = gradient;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // --- Text Drawing ---
    // Common text properties
    ctx.textAlign = 'center'; // All text blocks will be centered horizontally
    ctx.shadowColor = shadowColor;
    ctx.shadowBlur = shadowBlur;
    ctx.shadowOffsetX = shadowOffsetX;
    ctx.shadowOffsetY = shadowOffsetY;

    const padding = canvas.width * 0.05; // 5% padding from canvas edges for text
    const maxTextWidth = canvas.width - 2 * padding; // Max width for any text block

    // Draw Tagline (if provided)
    if (taglineText && taglineText.trim() !== "") {
        const taglineFontSize = Math.round(canvas.height * 0.035);
        const taglineLineHeight = taglineFontSize * 1.3; // Line height relative to font size
        // Set font: Size, Family, Generic Fallback
        ctx.font = `${taglineFontSize}px "${taglineFontFamily}", sans-serif`;
        ctx.fillStyle = taglineColor;
        // Y position: 18% from the top of the canvas for the center of the tagline block
        wrapText_internal(ctx, taglineText, canvas.width / 2, canvas.height * 0.18, maxTextWidth, taglineLineHeight);
    }

    // Draw Title
    const titleFontSize = Math.round(canvas.height * 0.12); // Larger font for title
    const titleLineHeight = titleFontSize * 1.35; // Script fonts might need slightly more line height
    ctx.font = `${titleFontSize}px "${titleFontFamily}", cursive`; // Cursive fallback
    ctx.fillStyle = titleColor;
    // Y position: 42% from the top for the center of the title block
    wrapText_internal(ctx, titleText, canvas.width / 2, canvas.height * 0.42, maxTextWidth, titleLineHeight);
    
    // Draw Author Name
    const authorFontSize = Math.round(canvas.height * 0.055);
    const authorLineHeight = authorFontSize * 1.3;
    ctx.font = `${authorFontSize}px "${authorFontFamily}", serif`; // Serif fallback
    ctx.fillStyle = authorColor;
    // Y position: 86% from the top (towards the bottom) for the center of the author block
    wrapText_internal(ctx, authorText, canvas.width / 2, canvas.height * 0.86, maxTextWidth, authorLineHeight);

    // Reset shadow for any subsequent drawing operations if needed elsewhere (good practice)
    ctx.shadowColor = 'transparent';
    ctx.shadowBlur = 0;
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;

    return canvas;
}

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 Romance Novel Cover Template Creator is a versatile tool that allows users to design customized covers for romance novels. With this tool, you can upload an image of your choice, add personalized text elements such as the title, author name, and an optional tagline, and apply various stylistic enhancements including font selection, colors, and text shadows. This tool is ideal for self-publishing authors, graphic designers, and anyone looking to create eye-catching book covers that stand out in a crowded market. Whether for print or digital formats, the template creator enables you to produce professional-quality covers to effectively capture readers’ attention.

Leave a Reply

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

Other Image Tools:

Image Tabloid Headline Template

Image Space Mission Patch Template Creator

Image Cassette Tape Cover Template Creator

Image Passport Page Template Generator

Image Old Map Frame With Compass Rose Decorator

Image Diploma and Degree Certificate Framer

Image Soviet Propaganda Poster Style Generator

Image Yu-Gi-Oh Card Template Creator

Image Ancient Roman Greek Tablet Frame Creator

Image Marriage Certificate Template Creator

Image Video Game Achievement Frame Creator

Image Newspaper Front Page Template Creator

Image Botanical Illustration Frame Creator

Image Vinyl Record Sleeve Template Creator

Vintage Photo Booth Strip Template Generator

Image Cyberpunk Interface Frame Designer

Image Detective Novel Cover Template

Image Achievement Certificate Framer

Image Illuminated Manuscript Frame Generator

Image Art Deco Poster Frame Creator

Image Egyptian Papyrus Scroll Frame Designer

Image Vintage Postage Stamp Frame Creator

Image Magic: The Gathering Card Frame Generator

Image Birth Certificate Template Generator

Image Driver’s License Template Creator

Image Scout Explorer Badge Template Creator

Image Steampunk Document Frame Creator

Image Vintage Scientific Illustration Frame Creator

Image Magazine Cover Template Creator

Image Album Cover Template Creator

Image Medieval Bestiary Page Template

Image Polaroid Instant Photo Frame

Image Pulp Fiction Book Cover Template

Image Medieval Manuscript Frame Creator

Image Vintage Advertisement Poster Generator

Victorian Image Calling Card Template

See All →