Disable implicit text wrapping
This commit is contained in:
+14
-19
@@ -107,31 +107,18 @@ def object_box(obj: dict[str, Any], scale: float) -> tuple[int, int, int, int]:
|
|||||||
return x, y, max(1, w), max(1, h)
|
return x, y, max(1, w), max(1, h)
|
||||||
|
|
||||||
|
|
||||||
def wrap_text(draw: ImageDraw.ImageDraw, text: str, font, max_width: int) -> list[str]:
|
def text_lines(text: str) -> list[str]:
|
||||||
lines: list[str] = []
|
return text.split("\n")
|
||||||
for raw_line in text.splitlines() or [""]:
|
|
||||||
current = ""
|
|
||||||
for word in raw_line.split(" "):
|
|
||||||
candidate = word if not current else f"{current} {word}"
|
|
||||||
if draw.textbbox((0, 0), candidate, font=font)[2] <= max_width:
|
|
||||||
current = candidate
|
|
||||||
continue
|
|
||||||
if current:
|
|
||||||
lines.append(current)
|
|
||||||
current = word
|
|
||||||
if current:
|
|
||||||
lines.append(current)
|
|
||||||
return lines
|
|
||||||
|
|
||||||
|
|
||||||
def draw_text(draw: ImageDraw.ImageDraw, obj: dict[str, Any], scale: float) -> None:
|
def draw_text(image: Image.Image, draw: ImageDraw.ImageDraw, obj: dict[str, Any], scale: float) -> None:
|
||||||
x, y, w, h = object_box(obj, scale)
|
x, y, w, h = object_box(obj, scale)
|
||||||
text = str(obj.get("text", "Text"))
|
text = str(obj.get("text", "Text"))
|
||||||
font_size = scaled_mm_to_px(float(obj.get("fontSizeMm", 5)), scale)
|
font_size = scaled_mm_to_px(float(obj.get("fontSizeMm", 5)), scale)
|
||||||
font = get_font(font_size, bool(obj.get("bold", False)))
|
font = get_font(font_size, bool(obj.get("bold", False)))
|
||||||
align = obj.get("align", "left")
|
align = obj.get("align", "left")
|
||||||
valign = obj.get("valign", "middle")
|
valign = obj.get("valign", "middle")
|
||||||
lines = wrap_text(draw, text, font, w)
|
lines = text_lines(text)
|
||||||
boxes = [draw.textbbox((0, 0), line, font=font) for line in lines]
|
boxes = [draw.textbbox((0, 0), line, font=font) for line in lines]
|
||||||
line_heights = [box[3] - box[1] for box in boxes]
|
line_heights = [box[3] - box[1] for box in boxes]
|
||||||
gap = max(1, round(font_size * 0.15))
|
gap = max(1, round(font_size * 0.15))
|
||||||
@@ -150,7 +137,15 @@ def draw_text(draw: ImageDraw.ImageDraw, obj: dict[str, Any], scale: float) -> N
|
|||||||
cx = x + w - text_w
|
cx = x + w - text_w
|
||||||
else:
|
else:
|
||||||
cx = x
|
cx = x
|
||||||
draw.text((cx, cy - box[1]), line, font=font, fill=1)
|
text_layer = Image.new("1", image.size, 0)
|
||||||
|
text_draw = ImageDraw.Draw(text_layer)
|
||||||
|
text_draw.text((cx, cy - box[1]), line, font=font, fill=1)
|
||||||
|
clip = Image.new("1", image.size, 0)
|
||||||
|
clip_draw = ImageDraw.Draw(clip)
|
||||||
|
clip_draw.rectangle([x, y, x + w, y + h], fill=1)
|
||||||
|
clipped = Image.new("1", image.size, 0)
|
||||||
|
clipped.paste(text_layer, mask=clip)
|
||||||
|
image.paste(1, mask=clipped)
|
||||||
cy += line_h + gap
|
cy += line_h + gap
|
||||||
|
|
||||||
|
|
||||||
@@ -283,7 +278,7 @@ def render_design_image(design: dict[str, Any]) -> Image.Image:
|
|||||||
continue
|
continue
|
||||||
obj_type = obj.get("type")
|
obj_type = obj.get("type")
|
||||||
if obj_type == "text" or obj_type == "symbol":
|
if obj_type == "text" or obj_type == "symbol":
|
||||||
draw_text(draw, obj, scale)
|
draw_text(image, draw, obj, scale)
|
||||||
elif obj_type == "shape":
|
elif obj_type == "shape":
|
||||||
draw_shape(draw, obj, scale)
|
draw_shape(draw, obj, scale)
|
||||||
elif obj_type == "line":
|
elif obj_type == "line":
|
||||||
|
|||||||
+4
-18
@@ -102,22 +102,8 @@ function clamp(value, min, max) {
|
|||||||
return Math.min(max, Math.max(min, value));
|
return Math.min(max, Math.max(min, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapCanvasText(text, maxWidthPx) {
|
function canvasTextLines(text) {
|
||||||
const lines = [];
|
return String(text || "").split("\n");
|
||||||
for (const rawLine of String(text || "").split("\n")) {
|
|
||||||
let current = "";
|
|
||||||
for (const word of rawLine.split(" ")) {
|
|
||||||
const candidate = current ? `${current} ${word}` : word;
|
|
||||||
if (ctx.measureText(candidate).width <= maxWidthPx) {
|
|
||||||
current = candidate;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (current) lines.push(current);
|
|
||||||
current = word;
|
|
||||||
}
|
|
||||||
lines.push(current);
|
|
||||||
}
|
|
||||||
return lines.length ? lines : [""];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function textNaturalWidthMm(obj) {
|
function textNaturalWidthMm(obj) {
|
||||||
@@ -149,7 +135,7 @@ function objectContentEndMm(obj) {
|
|||||||
const { value } = objectFont(obj);
|
const { value } = objectFont(obj);
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.font = value;
|
ctx.font = value;
|
||||||
const lines = wrapCanvasText(obj.text || "", mmToPx(obj.w));
|
const lines = canvasTextLines(obj.text || "");
|
||||||
const textWidthMm = Math.max(...lines.map((line) => pxToMm(ctx.measureText(line).width)), 0);
|
const textWidthMm = Math.max(...lines.map((line) => pxToMm(ctx.measureText(line).width)), 0);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
@@ -255,7 +241,7 @@ function drawTextObject(obj) {
|
|||||||
ctx.font = font;
|
ctx.font = font;
|
||||||
ctx.textBaseline = "middle";
|
ctx.textBaseline = "middle";
|
||||||
ctx.textAlign = obj.align || "left";
|
ctx.textAlign = obj.align || "left";
|
||||||
const lines = wrapCanvasText(obj.text || "", w);
|
const lines = canvasTextLines(obj.text || "");
|
||||||
const lineHeight = fontPx * 1.15;
|
const lineHeight = fontPx * 1.15;
|
||||||
const total = lineHeight * lines.length;
|
const total = lineHeight * lines.length;
|
||||||
let cy = y + h / 2 - total / 2 + lineHeight / 2;
|
let cy = y + h / 2 - total / 2 + lineHeight / 2;
|
||||||
|
|||||||
Reference in New Issue
Block a user