Doodle3D-Core/src/utils/textUtils.js

57 lines
1.7 KiB
JavaScript

import { SHAPE_CACHE_LIMIT } from '../constants/general.js';
import { Text } from '@doodle3d/cal';
import { POTRACE_OPTIONS } from '../constants/d2Constants.js';
import * as POTRACE from '@doodle3d/potrace-js';
import ClipperShape from '@doodle3d/clipper-js';
import { shapeToVectorShape } from './vectorUtils.js';
import memoize from 'memoizee';
const MARGIN = 200;
export const createText = memoize(createTextRaw, { max: SHAPE_CACHE_LIMIT });
export function createTextRaw(text, size, precision, family, style, weight) {
if (text === '') return [];
const canvas = createTextCanvas(text, size * precision, family, style, weight);
// TODO merge with potrace in flood fill trace reducer
const paths = POTRACE.getPaths(POTRACE.traceCanvas(canvas, POTRACE_OPTIONS));
const pathsOffset = paths.map(path => path.map(({ x, y }) => ({
x: (x - MARGIN) / precision,
y: (y - MARGIN) / precision
})));
const shapes = new ClipperShape(pathsOffset, true, true, false)
.fixOrientation()
.separateShapes()
.map(shape => shape.mapToLower())
.map(shapeToVectorShape);
return shapes;
}
const textContext = new Text({ baseline: 'top' });
export function createTextCanvas(text, size, family, style, weight) {
textContext.size = size;
textContext.family = family;
textContext.style = style;
textContext.weight = weight;
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = Math.ceil(textContext.measure(context, text)) + 2 * MARGIN;
const height = size + 2 * MARGIN;
canvas.width = width;
canvas.height = height;
context.fillStyle = 'white';
context.fillRect(0, 0, width, height);
textContext.drawText(context, text, MARGIN, MARGIN);
return canvas;
}