|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function writeIntoBubble(image: string, text: string): Promise<string> { |
|
const padding = 20; |
|
return new Promise((resolve, reject) => { |
|
const img = new Image(); |
|
img.onload = () => { |
|
const physicalWidth = img.width; |
|
const physicalHeight = img.height; |
|
const canvas = document.createElement('canvas'); |
|
const ctx = canvas.getContext('2d'); |
|
if (!ctx) { |
|
reject('Unable to get canvas context'); |
|
return; |
|
} |
|
canvas.width = physicalWidth; |
|
canvas.height = physicalHeight; |
|
ctx.drawImage(img, 0, 0, physicalWidth, physicalHeight); |
|
|
|
const imageData = ctx.getImageData(0, 0, physicalWidth, physicalHeight); |
|
const data = imageData.data; |
|
|
|
let minX = physicalWidth, minY = physicalHeight, maxX = 0, maxY = 0; |
|
|
|
for (let y = 0; y < physicalHeight; y++) { |
|
for (let x = 0; x < physicalWidth; x++) { |
|
const i = (y * physicalWidth + x) * 4; |
|
if (data[i] !== 255 || data[i + 1] !== 255 || data[i + 2] !== 255) { |
|
minX = Math.min(minX, x); |
|
minY = Math.min(minY, y); |
|
maxX = Math.max(maxX, x); |
|
maxY = Math.max(maxY, y); |
|
data[i] = data[i + 1] = data[i + 2] = 255; |
|
data[i + 3] = 255; |
|
} else { |
|
data[i + 3] = 0; |
|
} |
|
} |
|
} |
|
|
|
ctx.putImageData(imageData, 0, 0); |
|
|
|
ctx.save(); |
|
ctx.setTransform(1, 0, 0, 1, 0, 0); |
|
|
|
const textX = minX + padding; |
|
const textY = minY + padding; |
|
const textWidth = (maxX - minX) - 2 * padding; |
|
const textHeight = (maxY - minY) - 2 * padding; |
|
|
|
ctx.restore(); |
|
|
|
ctx.rect(textX, textY, textWidth, textHeight); |
|
ctx.clip(); |
|
|
|
let fontSize = 20; |
|
let lines = []; |
|
do { |
|
ctx.font = `${fontSize}px Comic Sans MS`; |
|
lines = wrapText(ctx, text, textWidth); |
|
fontSize -= 2; |
|
} while(lines.length > textHeight / fontSize && fontSize > 8); |
|
ctx.font = `${fontSize}px Comic Sans MS`; |
|
|
|
lines.forEach((line, i) => ctx.fillText(line, textX, textY + padding + i * fontSize)); |
|
|
|
resolve(canvas.toDataURL()); |
|
}; |
|
img.onerror = reject; |
|
img.src = image; |
|
}); |
|
} |
|
|
|
|
|
function wrapText(context: CanvasRenderingContext2D, text: string, maxWidth: number): string[] { |
|
const words = text.split(' '); |
|
const lines = []; |
|
let line = ''; |
|
|
|
for (let n = 0; n < words.length; n++) { |
|
const testLine = line + words[n] + ' '; |
|
const metrics = context.measureText(testLine); |
|
const testWidth = metrics.width; |
|
if (testWidth > maxWidth && n > 0) { |
|
lines.push(line); |
|
line = words[n] + ' '; |
|
} else { |
|
line = testLine; |
|
} |
|
} |
|
lines.push(line); |
|
return lines; |
|
} |