Please bookmark this page to avoid losing your image tool!

Image Explorer’s Journal Page Template

(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,
    pageTitle = "Explorer's Journal",
    dateLabelText = "Date:",
    locationLabelText = "Location:",
    notesHeadingText = "Observations & Notes:"
) {
    // --- Font Family Constant ---
    const FONT_FAMILY = "Caveat"; // A nice handwritten-style font

    // --- Helper Function to Load Google Font ---
    async function loadGoogleFont(fontName, weights = ['400', '700']) {
        const fontId = `google-font-${fontName.replace(/\s+/g, '-')}`;

        if (document.getElementById(fontId)) {
            // Link tag exists, implies font loading was already initiated.
            // We just need to check/wait for its readiness.
            return document.fonts.load(`1em "${fontName}"`)
                .then(() => {
                    // console.log(`Font "${fontName}" confirmed ready (pre-existing request).`);
                    return true;
                })
                .catch(() => {
                    // console.warn(`Font "${fontName}" (pre-existing request) not ready or failed to load.`);
                    return false; // Signal that font is not available
                });
        }

        return new Promise((resolve) => { // Simplified to always resolve, returning true/false
            const link = document.createElement('link');
            link.id = fontId;
            link.rel = 'stylesheet';
            let fontQuery = fontName.replace(/\s+/g, '+');
            if (weights && weights.length > 0) {
                fontQuery += `:wght@${weights.join(';')}`;
            }
            link.href = `https://fonts.googleapis.com/css2?family=${fontQuery}&display=swap`;

            link.onload = async () => {
                try {
                    await document.fonts.load(`1em "${fontName}"`); // Ensure font is usable
                    // console.log(`Font "${fontName}" loaded and ready.`);
                    resolve(true);
                } catch (e) {
                    // console.error(`Error confirming font "${fontName}" readiness after stylesheet load:`, e);
                    resolve(false); // Font might not be the one requested, but proceed with fallback
                }
            };
            link.onerror = () => {
                // console.error(`Failed to load Google Font stylesheet for "${fontName}".`);
                resolve(false); // Critical failure to load stylesheet, proceed with fallback
            };
            document.head.appendChild(link);
        });
    }

    // --- Load The Chosen Font ---
    // We don't strictly need to await the result if we're okay with a flash of unstyled text (FOUT),
    // but for canvas rendering, it's crucial the font is ready before drawing text.
    await loadGoogleFont(FONT_FAMILY, ['400', '700']); // Load regular and bold weights


    // --- Canvas Setup ---
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // --- Page & Style Constants ---
    const PAGE_WIDTH = 800;
    const PAGE_HEIGHT = 1100;
    const MARGIN = 50;
    const CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN;

    const BACKGROUND_COLOR = '#FDF5E6'; // Old Lace (Paper color)
    const TEXT_COLOR = '#4A3B31';      // Dark brown (Ink color)
    const LINE_COLOR = '#D2B48C';      // Tan (Lighter lines for ruling)
    const IMAGE_BORDER_COLOR = '#5D4037'; // Darker brown for image border

    const FONT_TITLE_SIZE = 36;
    const FONT_HEADING_SIZE = 24;
    const FONT_TEXT_SIZE = 20;
    const TEXT_LINE_SPACING = FONT_TEXT_SIZE * 1.6; // Spacing for handwritten notes lines

    const FONT_STRING = (size, weight = "normal") => `${weight} ${size}px "${FONT_FAMILY}", cursive, sans-serif`;

    canvas.width = PAGE_WIDTH;
    canvas.height = PAGE_HEIGHT;

    // --- 1. Draw Background ---
    ctx.fillStyle = BACKGROUND_COLOR;
    ctx.fillRect(0, 0, PAGE_WIDTH, PAGE_HEIGHT);

    let currentY = MARGIN;

    // --- 2. Draw Page Title ---
    ctx.fillStyle = TEXT_COLOR;
    ctx.font = FONT_STRING(FONT_TITLE_SIZE, "bold");
    ctx.textAlign = 'center';
    // Adjust Y for better visual centering of text typical in Caviat font
    ctx.fillText(pageTitle, PAGE_WIDTH / 2, currentY + FONT_TITLE_SIZE * 0.8);
    currentY += FONT_TITLE_SIZE + 30;

    // --- 3. Date and Location Fields ---
    ctx.font = FONT_STRING(FONT_TEXT_SIZE);
    ctx.textAlign = 'left';
    ctx.fillStyle = TEXT_COLOR;

    const fieldTextBaselineY = currentY + FONT_TEXT_SIZE * 0.8; // Adjusted for Caviat
    const fieldLineYOffset = FONT_TEXT_SIZE * 0.2 + fieldTextBaselineY; // Line below baseline
    const labelToLineSpacing = 10; // Space between text label and start of line

    // Date Field
    const dateLabelWidth = ctx.measureText(dateLabelText).width;
    ctx.fillText(dateLabelText, MARGIN, fieldTextBaselineY);
    ctx.beginPath();
    ctx.moveTo(MARGIN + dateLabelWidth + labelToLineSpacing, fieldLineYOffset);
    ctx.lineTo(MARGIN + CONTENT_WIDTH / 2 - 20, fieldLineYOffset);
    ctx.strokeStyle = LINE_COLOR;
    ctx.lineWidth = 1.5;
    ctx.stroke();

    // Location Field
    const locationLabelWidth = ctx.measureText(locationLabelText).width;
    ctx.fillText(locationLabelText, MARGIN + CONTENT_WIDTH / 2, fieldTextBaselineY);
    ctx.beginPath();
    ctx.moveTo(MARGIN + CONTENT_WIDTH / 2 + locationLabelWidth + labelToLineSpacing, fieldLineYOffset);
    ctx.lineTo(MARGIN + CONTENT_WIDTH, fieldLineYOffset);
    ctx.stroke();

    currentY += FONT_TEXT_SIZE + 30;

    // --- 4. Draw Image Area ---
    const imageContainerX = MARGIN;
    const imageContainerY = currentY;
    const imageContainerWidth = CONTENT_WIDTH;
    const imageContainerHeight = PAGE_HEIGHT * 0.33; // Max height for the image display area

    // Calculate image draw dimensions to fit and maintain aspect ratio
    let drawW = originalImg.width;
    let drawH = originalImg.height;
    const imgAspect = originalImg.width / originalImg.height;
    const containerAspect = imageContainerWidth / imageContainerHeight;

    if (imgAspect > containerAspect) { // Image is wider than container space
        drawW = imageContainerWidth;
        drawH = drawW / imgAspect;
    } else { // Image is taller than container space (or perfectly matches aspect)
        drawH = imageContainerHeight;
        drawW = drawH * imgAspect;
    }

    // Center image within its allocated container space
    const imgActualX = imageContainerX + (imageContainerWidth - drawW) / 2;
    const imgActualY = imageContainerY + (imageContainerHeight - drawH) / 2;

    // "Pasted Paper" effect for the image
    const paperPadding = 8;
    ctx.fillStyle = '#FEFBF3'; // Slightly off-white, like photo paper
    ctx.shadowColor = 'rgba(0,0,0,0.25)';
    ctx.shadowBlur = 10;
    ctx.shadowOffsetX = 5;
    ctx.shadowOffsetY = 5;
    // Draw the "paper" behind the image
    ctx.fillRect(
        imgActualX - paperPadding / 2,
        imgActualY - paperPadding / 2,
        drawW + paperPadding,
        drawH + paperPadding
    );

    // Reset shadow for subsequent drawings
    ctx.shadowColor = 'transparent';
    ctx.shadowBlur = 0;
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;

    // Draw the actual image
    ctx.drawImage(originalImg, imgActualX, imgActualY, drawW, drawH);

    // Add a thin border line around the image
    ctx.strokeStyle = IMAGE_BORDER_COLOR;
    ctx.lineWidth = 1;
    ctx.strokeRect(imgActualX, imgActualY, drawW, drawH);

    currentY += imageContainerHeight + 35; // Space after image area

    // --- 5. "Observations & Notes" Heading ---
    // Ensure there's space for heading and at least two lines of notes
    if (currentY < PAGE_HEIGHT - MARGIN - (FONT_HEADING_SIZE + TEXT_LINE_SPACING * 2)) {
        ctx.font = FONT_STRING(FONT_HEADING_SIZE, "bold");
        ctx.textAlign = 'left';
        ctx.fillStyle = TEXT_COLOR;
        ctx.fillText(notesHeadingText, MARGIN, currentY + FONT_HEADING_SIZE * 0.8);
        currentY += FONT_HEADING_SIZE + 20; // Space after heading
    }

    // --- 6. Journal Lines for Writing ---
    ctx.strokeStyle = LINE_COLOR;
    ctx.lineWidth = 1;

    // Stop drawing lines if too close to the bottom margin
    const bottomLimit = PAGE_HEIGHT - MARGIN - TEXT_LINE_SPACING / 2;

    while (currentY < bottomLimit) {
        ctx.beginPath();
        ctx.moveTo(MARGIN, currentY);
        ctx.lineTo(MARGIN + CONTENT_WIDTH, currentY);
        ctx.stroke();
        currentY += TEXT_LINE_SPACING;
    }

    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 Explorer’s Journal Page Template tool allows users to create a personalized journal page designed for documenting exploration experiences. Users can input an image, along with a title, date, location, and observations, creating a visually appealing and organized layout. This tool is perfect for travelers, nature enthusiasts, and researchers looking to keep detailed notes and memories in a structured format that mimics a handwritten journal page. The generated journal page features stylish fonts, drawing areas for images, and guided sections for notes, making it a valuable resource for both personal and professional documentation.

Leave a Reply

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