function renderText(ctx, textObj) {
ctx.save();
// Move origin to text position
ctx.translate(textObj.x, textObj.y);
ctx.rotate(
textObj.rotation * Math.PI / 180);
// Configure font
ctx.font =
`${textObj.bold ? 'bold ' : ''}` +
`${textObj.size}px ${textObj.font}`;
ctx.fillStyle = textObj.color;
ctx.textAlign = textObj.align;
ctx.textBaseline = 'top';
ctx.globalAlpha = textObj.opacity;
// Draw text
ctx.fillText(textObj.text, 0, 0);
// Optional: stroke outline
if (textObj.stroke) {
ctx.strokeStyle =
textObj.strokeColor;
ctx.lineWidth =
textObj.strokeWidth;
ctx.strokeText(textObj.text, 0, 0);
}
ctx.restore();
}
async function loadGoogleFont(
fontFamily
) {
const url =
`https://fonts.googleapis.com/` +
`css2?family=` +
`${fontFamily.replace(/ /g, '+')}`;
// Inject stylesheet
const link =
document.createElement('link');
link.href = url;
link.rel = 'stylesheet';
document.head.appendChild(link);
// Wait for font to actually load
await document.fonts.load(
`16px "${fontFamily}"`
);
return fontFamily;
}
// Usage
await loadGoogleFont('Lobster');
ctx.font = '48px Lobster';
ctx.fillText('Hello!', 100, 100);
function TextOverlay({
position, font, size, color, onConfirm
}) {
const ref = useRef(null);
useEffect(() => {
ref.current?.focus();
}, []);
const handleBlur = () => {
const text =
ref.current.innerText.trim();
if (text) onConfirm(text);
};
return (
<div
ref={ref}
contentEditable
suppressContentEditableWarning
onBlur={handleBlur}
onKeyDown={(e) => {
if (e.key === 'Enter'
&& !e.shiftKey) {
e.preventDefault();
ref.current.blur();
}
}}
style={{
position: 'absolute',
left: position.x,
top: position.y,
font: `${size}px ${font}`,
color: color,
outline: 'none',
minWidth: '100px',
border: '1px dashed #fff4',
}}
/>
);
}
function getTextBounds(ctx, textObj) {
ctx.font =
`${textObj.size}px ${textObj.font}`;
const metrics =
ctx.measureText(textObj.text);
return {
x: textObj.x,
y: textObj.y,
width: metrics.width,
height: textObj.size * 1.2
};
}
function isClickOnText(
mouseX, mouseY, bounds
) {
return (
mouseX >= bounds.x &&
mouseX <= bounds.x + bounds.width &&
mouseY >= bounds.y &&
mouseY <= bounds.y + bounds.height
);
}
git switch -c feature/PIXELCRAFT-063-text-tool
git add src/
git commit -m "Add text tool with fonts, rotation, drag (PIXELCRAFT-063)"
git push origin feature/PIXELCRAFT-063-text-tool
# PR → Review → Merge → Close ticket ✅
Fonts are one of the most complex data structures in computing.
A single font file contains thousands of Bézier curves, kerning tables, ligatures, and hinting instructions.