It works! 
Thank you very much!
You're the best!
If I had one suggestion, it would be nice if there was another lineHeight feature.
If you have free time, check this code and watch the video clip attached!
<!-- zimjs.com - JavaScript Canvas Framework -->
<script type="module">
import zim from "https://zimjs.org/cdn/019/zim";
new Frame(FULL, null, null, light, dark, ready);
function ready() {
let A; let B; let C;
function test (){
if (A != null) {
A.removeFrom();
A = null;
}
if (B != null) {
B.removeFrom();
B = null;
}
if (C != null) {
C.removeFrom();
C = null;
}
A = new Label({
text: "こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?",
labelWidth: W * 0.7,
labelHeight: 150,
lineHeight: 35,
align: "center",
splitWords: true,
}).center(S).mov(0, -300)
B = generateText({
text: "こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?こんに ちは。最近aどうですか?",
maxWidth: W * 0.7,
maxHeight: 150,
lineHeightRatio: 1.4,
textAlign: "center",
}).center(S).mov(0, 0)
C = generateText({
text: "Happy new year! Happy new Safawoiefawigkso. Happy new year! Happy new Safawoiefawigkso. Happy new year! Happy new Safawoiefawigkso.",
maxWidth: W * 0.7,
maxHeight: 150,
lineHeightRatio: 1.4,
textAlign: "center",
splitByCharacter: false,
}).center(S).mov(0, 300)
}
test()
F.on("resize", () => {
test()
});
function generateText({
text = "Text",
maxWidth = 300,
maxHeight = 300,
fontName,
color,
maxFontSize = 80,
lineHeightRatio = 1.4,
textAlign = "center",
splitByCharacter = true,
minFontSize = 10
}) {
// Create the ZIM Label
const label = new Label({
text: "",
size: maxFontSize,
font: fontName,
color: color
});
// Access the internal CreateJS Text object
const internalText = label.label;
internalText.textAlign = textAlign;
internalText.textBaseline = "middle";
// Create a dummy Text object for measuring width/height
const measureObj = new createjs.Text("", "", color);
// Function: Wrap text by character (splits character by character)
function wrapByCharacter(measurer, rawString, limitWidth) {
const lines = [];
let currentLine = "";
for (let i = 0; i < rawString.length; i++) {
const char = rawString[i];
measurer.text = currentLine + char;
// Check if adding the character exceeds the width
if (currentLine !== "" && measurer.getMeasuredWidth() > limitWidth) {
lines.push(currentLine);
currentLine = char;
} else {
currentLine += char;
}
}
if (currentLine !== "") lines.push(currentLine);
return lines;
}
// Function: Wrap text by word (keeps words intact unless too long)
function wrapByWord(measurer, rawString, limitWidth) {
// Split by whitespace but keep the token logic
const tokens = rawString.match(/\S+\s*/g) || [""];
const lines = [];
let currentLine = "";
for (const rawToken of tokens) {
const token = rawToken;
measurer.text = currentLine + token;
// Check if adding the token exceeds the width
if (currentLine !== "" && measurer.getMeasuredWidth() > limitWidth) {
// Push the current line (trim trailing spaces)
lines.push(currentLine.replace(/\s+$/g, ""));
// Handle the next token (remove leading spaces)
let nextToken = token.replace(/^\s+/g, "");
measurer.text = nextToken;
// If the single token itself is wider than the limit, force character split
if (nextToken !== "" && measurer.getMeasuredWidth() > limitWidth) {
const charLines = wrapByCharacter(measurer, nextToken, limitWidth);
if (charLines.length > 1) {
lines.push(...charLines.slice(0, -1));
currentLine = charLines[charLines.length - 1] || "";
} else {
currentLine = charLines[0] || "";
}
} else {
currentLine = nextToken;
}
} else {
currentLine += token;
}
}
if (currentLine !== "") lines.push(currentLine.replace(/\s+$/g, ""));
return lines;
}
// Function: Calculate line breaks and total height
function calculateLayout(measurer, rawString, limitWidth, lineHeight, isCharSplit) {
const lines = isCharSplit
? wrapByCharacter(measurer, rawString, limitWidth)
: wrapByWord(measurer, rawString, limitWidth);
return {
wrappedText: lines.join("\n"),
totalHeight: lines.length * lineHeight
};
}
// Try font sizes from largest to smallest
for (let size = maxFontSize; size >= minFontSize; size--) {
const fontString = `${size}px ${fontName}`;
const currentLineHeight = size * lineHeightRatio;
measureObj.font = fontString;
const result = calculateLayout(measureObj, text, maxWidth, currentLineHeight, splitByCharacter);
if (result.totalHeight <= maxHeight) {
// Apply successful settings
internalText.font = fontString;
internalText.lineHeight = currentLineHeight;
label.text = result.wrappedText;
// Horizontal Alignment
if (textAlign === "center") {
label.x = maxWidth / 2;
} else if (textAlign === "right") {
label.x = maxWidth;
} else {
label.x = 0;
}
// Vertical Alignment (Center)
label.y = (maxHeight - result.totalHeight) / 2;
return label;
}
}
// If text doesn't fit even at min size, enforce minimum settings
internalText.font = `${minFontSize}px ${fontName}`;
internalText.lineHeight = minFontSize * lineHeightRatio;
label.text = text;
if (textAlign === "center") {
label.x = maxWidth / 2;
} else if (textAlign === "right") {
label.x = maxWidth;
} else {
label.x = 0;
}
label.y = 0;
return label;
}
} // end ready
</script>
<meta name="viewport" content="width=device-width, user-scalable=no" />