/**
* Writing/Tracing Worksheet Generator
* Generates consistent dotted letters and numbers for tracing practice
*/
// Letter path definitions (school-style uppercase)
const letterPaths = {
'A': 'M 0 80 L 25 0 L 50 80 M 12 50 L 38 50',
'B': 'M 0 0 L 0 80 L 35 80 Q 50 80 50 60 Q 50 40 35 40 L 0 40 M 0 0 L 35 0 Q 50 0 50 20 Q 50 40 35 40',
'C': 'M 50 15 Q 50 0 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 65',
'D': 'M 0 0 L 0 80 L 25 80 Q 50 80 50 40 Q 50 0 25 0 L 0 0',
'E': 'M 50 0 L 0 0 L 0 80 L 50 80 M 0 40 L 35 40',
'F': 'M 50 0 L 0 0 L 0 80 M 0 40 L 35 40',
'G': 'M 50 15 Q 50 0 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 50 L 30 50',
'H': 'M 0 0 L 0 80 M 50 0 L 50 80 M 0 40 L 50 40',
'I': 'M 10 0 L 40 0 M 25 0 L 25 80 M 10 80 L 40 80',
'J': 'M 10 0 L 40 0 M 30 0 L 30 60 Q 30 80 15 80 Q 0 80 0 65',
'K': 'M 0 0 L 0 80 M 50 0 L 0 45 L 50 80',
'L': 'M 0 0 L 0 80 L 50 80',
'M': 'M 0 80 L 0 0 L 25 40 L 50 0 L 50 80',
'N': 'M 0 80 L 0 0 L 50 80 L 50 0',
'O': 'M 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 40 Q 50 0 25 0',
'P': 'M 0 80 L 0 0 L 35 0 Q 50 0 50 20 Q 50 40 35 40 L 0 40',
'Q': 'M 25 0 Q 0 0 0 40 Q 0 80 25 80 Q 50 80 50 40 Q 50 0 25 0 M 35 60 L 55 85',
'R': 'M 0 80 L 0 0 L 35 0 Q 50 0 50 20 Q 50 40 35 40 L 0 40 M 30 40 L 50 80',
'S': 'M 50 15 Q 50 0 25 0 Q 0 0 0 20 Q 0 40 25 40 Q 50 40 50 60 Q 50 80 25 80 Q 0 80 0 65',
'T': 'M 0 0 L 50 0 M 25 0 L 25 80',
'U': 'M 0 0 L 0 60 Q 0 80 25 80 Q 50 80 50 60 L 50 0',
'V': 'M 0 0 L 25 80 L 50 0',
'W': 'M 0 0 L 12 80 L 25 30 L 38 80 L 50 0',
'X': 'M 0 0 L 50 80 M 50 0 L 0 80',
'Y': 'M 0 0 L 25 40 L 50 0 M 25 40 L 25 80',
'Z': 'M 0 0 L 50 0 L 0 80 L 50 80'
};
// Number path definitions
const numberPaths = {
'1': 'M 15 15 L 25 0 L 25 80 M 10 80 L 40 80',
'2': 'M 0 20 Q 0 0 25 0 Q 50 0 50 20 Q 50 40 0 80 L 50 80',
'3': 'M 0 10 Q 0 0 25 0 Q 50 0 50 20 Q 50 40 25 40 Q 50 40 50 60 Q 50 80 25 80 Q 0 80 0 70',
'4': 'M 40 80 L 40 0 L 0 55 L 50 55',
'5': 'M 50 0 L 0 0 L 0 35 Q 0 35 25 35 Q 50 35 50 57 Q 50 80 25 80 Q 0 80 0 65',
'6': 'M 45 10 Q 45 0 25 0 Q 0 0 0 40 L 0 55 Q 0 80 25 80 Q 50 80 50 57 Q 50 35 25 35 Q 0 35 0 55',
'7': 'M 0 0 L 50 0 L 20 80',
'8': 'M 25 40 Q 0 40 0 20 Q 0 0 25 0 Q 50 0 50 20 Q 50 40 25 40 Q 0 40 0 60 Q 0 80 25 80 Q 50 80 50 60 Q 50 40 25 40',
'9': 'M 5 70 Q 5 80 25 80 Q 50 80 50 40 L 50 25 Q 50 0 25 0 Q 0 0 0 23 Q 0 45 25 45 Q 50 45 50 25'
};
/**
* Generate a dotted path for a single character
*/
function generateDottedChar(char, x, y, scale = 1, color = '#000000') {
const path = letterPaths[char.toUpperCase()] || numberPaths[char];
if (!path) return '';
// Transform the path to the correct position and scale
const transformedPath = path.replace(/(\d+)/g, (match) => {
return match; // Keep original values, we'll use transform
});
return ``;
}
/**
* Generate a row of dotted characters centered on the page with dynamic scaling
*/
function generateCharacterRow(text, y, baseScale = 1, color = '#000000') {
const baseCharWidth = 50;
const baseSpacing = 20;
const maxWidth = 460; // Leave 20px margin on each side
// Calculate required width with base scale
const requiredWidth = text.length * (baseCharWidth + baseSpacing) * baseScale - baseSpacing * baseScale;
// Scale down if content is too wide
let finalScale = baseScale;
if (requiredWidth > maxWidth) {
finalScale = maxWidth / (text.length * (baseCharWidth + baseSpacing) - baseSpacing);
}
const charWidth = baseCharWidth * finalScale;
const spacing = baseSpacing * finalScale;
const totalWidth = text.length * charWidth + (text.length - 1) * spacing;
let startX = (500 - totalWidth) / 2;
let svg = '';
for (let i = 0; i < text.length; i++) {
const char = text[i];
svg += generateDottedChar(char, startX + i * (charWidth + spacing), y, finalScale, color);
}
return svg;
}
/**
* Generate baseline guide
*/
function generateBaseline(y, width = 400) {
const startX = (500 - width) / 2;
return ``;
}
/**
* Generate complete writing worksheet
* @param {Object} params
* @param {string} params.word - Word to trace (if tracing letters)
* @param {number[]} params.numbers - Numbers to trace (if tracing numbers)
* @param {boolean} params.isNumbers - Whether to trace numbers
* @param {number} params.age - Child's age (affects complexity)
*/
export function generateWritingWorksheet({ word, numbers, isNumbers, age }) {
const content = isNumbers ? numbers.join('') : word.toUpperCase();
const title = isNumbers ? 'Trace the Numbers' : `Trace: ${word.toUpperCase()}`;
const svg = ``;
return {
title: title,
subtitle: isNumbers ? 'Practice writing your numbers!' : 'Practice writing letters!',
sections: [{
id: 'writing-main',
type: 'writing',
content_svg: svg
}]
};
}
export default { generateWritingWorksheet };