@chemicalluck/canvas-txt
Version:
Render multiline textboxes in HTML5 canvas with auto line breaks and better alignment system
128 lines (127 loc) • 3.58 kB
JavaScript
function k({
ctx: e,
line: c,
spaceWidth: p,
spaceChar: n,
width: a
}) {
const i = c.trim(), s = i.split(/\s+/), o = s.length - 1;
if (o === 0)
return i;
const m = e.measureText(s.join("")).width, d = (a - m) / p, b = Math.floor(d / o);
if (d < 1)
return i;
const r = n.repeat(b);
return s.join(r);
}
const W = " ";
function H({
ctx: e,
text: c,
justify: p,
width: n
}) {
const a = /* @__PURE__ */ new Map(), i = (r) => {
let g = a.get(r);
return g !== void 0 || (g = e.measureText(r).width, a.set(r, g)), g;
};
let s = [], o = c.split(`
`);
const m = p ? i(W) : 0;
let d = 0, b = 0;
for (const r of o) {
let g = i(r);
const y = r.length;
if (g <= n) {
s.push(r);
continue;
}
let h = r, t, f, l = "";
for (; g > n; ) {
if (d++, t = b, f = t === 0 ? 0 : i(r.substring(0, t)), f < n)
for (; f < n && t < y && (t++, f = i(h.substring(0, t)), t !== y); )
;
else if (f > n)
for (; f > n && (t = Math.max(1, t - 1), f = i(h.substring(0, t)), t !== 1); )
;
if (b = Math.round(
b + (t - b) / d
), t--, t > 0) {
let u = t;
if (h.substring(u, u + 1) != " ") {
for (; u >= 0 && h.substring(u, u + 1) != " "; )
u--;
u > 0 && (t = u);
}
}
t === 0 && (t = 1), l = h.substring(0, t), l = p ? k({
ctx: e,
line: l,
spaceWidth: m,
spaceChar: W,
width: n
}) : l, s.push(l), h = h.substring(t), g = i(h);
}
g > 0 && (l = p ? k({
ctx: e,
line: h,
spaceWidth: m,
spaceChar: W,
width: n
}) : h, s.push(l));
}
return s;
}
function w({
ctx: e,
text: c,
style: p
}) {
const n = e.textBaseline, a = e.font;
e.textBaseline = "bottom", e.font = p;
const { actualBoundingBoxAscent: i } = e.measureText(c);
return e.textBaseline = n, e.font = a, i;
}
const C = {
debug: !1,
align: "center",
vAlign: "middle",
drawStyle: "fill",
fontSize: 14,
fontWeight: "",
fontStyle: "",
fontVariant: "",
font: "Arial",
lineHeight: null,
justify: !1
};
function E(e, c, p) {
const { width: n, height: a, x: i, y: s } = p, o = { ...C, ...p };
if (n <= 0 || a <= 0 || o.fontSize <= 0)
return { height: 0 };
const m = i + n, d = s + a, { fontStyle: b, fontVariant: r, fontWeight: g, fontSize: y, font: h } = o, t = `${b} ${r} ${g} ${y}px ${h}`;
e.font = t;
let f = s + a / 2 + o.fontSize / 2, l;
o.align === "right" ? (l = m, e.textAlign = "right") : o.align === "left" ? (l = i, e.textAlign = "left") : (l = i + n / 2, e.textAlign = "center");
const u = H({
ctx: e,
text: c,
justify: o.justify,
width: n
}), A = o.lineHeight ? o.lineHeight : w({ ctx: e, text: "M", style: t }), v = A * (u.length - 1), B = v / 2;
let S = s;
o.vAlign === "top" ? (e.textBaseline = "top", f = s) : o.vAlign === "bottom" ? (e.textBaseline = "bottom", f = d - v, S = d) : (e.textBaseline = "bottom", S = s + a / 2, f -= B);
const P = o.drawStyle === "fill" ? e.fillText.bind(e) : e.strokeText.bind(e);
if (u.forEach((T) => {
T = T.trim(), P(T, l, f), f += A;
}), o.debug) {
const T = "#0C8CE9";
e.lineWidth = 1, e.strokeStyle = T, e.strokeRect(i, s, n, a), e.lineWidth = 1, e.strokeStyle = T, e.beginPath(), e.moveTo(l, s), e.lineTo(l, d), e.stroke(), e.strokeStyle = T, e.beginPath(), e.moveTo(i, S), e.lineTo(m, S), e.stroke();
}
return { height: v + A };
}
export {
E as drawText,
w as getTextHeight,
H as splitText
};