add text align options, fix text padding

This commit is contained in:
Aleksander Majda
2025-08-29 10:44:11 +02:00
parent 6e6b02aa26
commit 1fb8a422f9
+80 -42
View File
@@ -10,6 +10,7 @@ class BookToolsTextToImage:
self.device = device
_positions = ["top", "bottom"]
_alignments = ["left", "center"]
_text_justifications = ["left", "center", "right"]
_colors = [
(255, 50, 50),
@@ -81,6 +82,8 @@ class BookToolsTextToImage:
"background_style": (["none", "rectangle", "ribbon", "glow"], {"default": "none"}),
"background_color": (["auto", "dark", "light"], {"default": "auto"}),
"background_padding": ("INT", {"default": 20, "min": 0, "max": 100}),
"text_justification": (["left", "center", "right"], {"default": "left"}),
"vertical_position": (["top", "center", "bottom"], {"default": "center"}),
}
}
@@ -100,7 +103,7 @@ class BookToolsTextToImage:
# Fallback to simple width calculation
return sum(font.getbbox(char)[2] - font.getbbox(char)[0] for char in text)
def calculate_text_size(self, text, font_size, font_path, max_width, max_height, line_height_factor, char_spacing_factor):
def calculate_text_size(self, text, font_size, font_path, max_width, max_height, line_height_factor, char_spacing_factor, text_justification="left"):
try:
font = ImageFont.truetype(font_path, font_size)
except Exception as e:
@@ -117,45 +120,51 @@ class BookToolsTextToImage:
for paragraph in paragraphs:
words = paragraph.split()
current_line = []
current_width = 0
for word in words:
# measure word width using char_spacing
word_width = 0
for char in word:
# Test if adding this word would exceed the line width
test_line = current_line + [word]
test_text = ' '.join(test_line)
# Calculate test line width with character spacing (same method as draw_text_with_border)
test_width = 0
for i, char in enumerate(test_text):
bbox = font.getbbox(char)
word_width += (bbox[2] - bbox[0]) + char_spacing
test_width += (bbox[2] - bbox[0])
if i < len(test_text) - 1: # Add spacing between chars, but not after last
test_width += char_spacing
# Remove extra spacing after the last character
if word:
word_width -= char_spacing
# Consider space width
space_width = font.getbbox(' ')[2] if current_line else 0
if current_width + word_width + space_width <= max_width:
if test_width <= max_width:
# Word fits, add it to current line
current_line.append(word)
current_width += word_width + space_width
else:
# Word doesn't fit, finish current line and start new one
if current_line:
line_text = ' '.join(current_line)
lines.append(line_text)
# Calculate line width consistently
line_width = 0
for char in line_text:
for i, char in enumerate(line_text):
bbox = font.getbbox(char)
line_width += (bbox[2] - bbox[0]) + char_spacing
line_width += (bbox[2] - bbox[0])
if i < len(line_text) - 1: # Only add spacing between chars, not after last
line_width += char_spacing
max_line_width = max(max_line_width, line_width)
# Start new line with current word
current_line = [word]
current_width = word_width
# Add the last line if it exists
if current_line:
line_text = ' '.join(current_line)
lines.append(line_text)
# Calculate line width consistently
line_width = 0
for char in line_text:
for i, char in enumerate(line_text):
bbox = font.getbbox(char)
line_width += (bbox[2] - bbox[0]) + char_spacing
line_width += (bbox[2] - bbox[0])
if i < len(line_text) - 1: # Only add spacing between chars, not after last
line_width += char_spacing
max_line_width = max(max_line_width, line_width)
line_height = int(font_size * line_height_factor)
@@ -166,6 +175,35 @@ class BookToolsTextToImage:
return lines, fits, max_line_width, total_height
def calculate_line_x_position(self, line, font, justification, container_width, padding, char_spacing_factor):
"""Calculate x position for a line based on justification"""
# Calculate total line width including character spacing (same as draw_text_with_border)
total_width = 0
for i, char in enumerate(line):
bbox = font.getbbox(char)
char_width = bbox[2] - bbox[0]
total_width += char_width
if i < len(line) - 1: # Add spacing between chars, but not after last char
total_width += font.size * char_spacing_factor
# Calculate effective width (considering only main padding)
effective_width = container_width - (2 * padding)
if justification == "left":
return padding
elif justification == "center":
# Center within the effective area, but ensure it doesn't go outside boundaries
center_x = padding + (effective_width - total_width) // 2
# Ensure text doesn't go outside left or right boundaries
min_x = padding
max_x = container_width - total_width - padding
return max(min_x, min(max_x, center_x))
elif justification == "right":
return container_width - total_width - padding
else:
return padding # Default to left
def create_gradient_color(self, color, height, direction="vertical"):
start_color = color
end_color = tuple(max(0, c - 80) for c in color) # Darker version
@@ -412,7 +450,8 @@ class BookToolsTextToImage:
line_height_factor, curve_amount=0.0, char_spacing=0.2,
random_position=False, position_offset=20, shadow_offset=4,
outline_thickness=3, use_gradient=False, gradient_direction="vertical",
background_style="none", background_color="auto", background_padding=20):
background_style="none", background_color="auto", background_padding=20,
text_justification="left", vertical_position="center"):
image_tensor = image
image_np = image_tensor.cpu().numpy()
image = Image.fromarray((image_np.squeeze(0) * 255).astype(np.uint8))
@@ -433,7 +472,7 @@ class BookToolsTextToImage:
# First check if min_font_size fits
lines, fits, actual_width, actual_height = self.calculate_text_size(
text, min_font_size, font, effective_width, effective_height, line_height_factor, char_spacing
text, min_font_size, font, effective_width, effective_height, line_height_factor, char_spacing, text_justification
)
if fits:
@@ -441,7 +480,7 @@ class BookToolsTextToImage:
while low <= high:
mid = (low + high) // 2
lines, fits, actual_width, actual_height = self.calculate_text_size(
text, mid, font, effective_width, effective_height, line_height_factor, char_spacing
text, mid, font, effective_width, effective_height, line_height_factor, char_spacing, text_justification
)
if fits:
@@ -478,8 +517,13 @@ class BookToolsTextToImage:
base_y += random.randint(-10, 10)
else:
base_y = (height - total_height) // 2
horizontal_pos = "center"
# Use the vertical_position parameter (consistent with effective area calculation)
if vertical_position == "top":
base_y = padding
elif vertical_position == "bottom":
base_y = height - total_height - padding
else: # center
base_y = padding + (effective_height - total_height) // 2
y = base_y
text_color = random.choice(self._colors)
@@ -494,24 +538,18 @@ class BookToolsTextToImage:
self.draw_curved_text(draw, line, loaded_font, width//2, y, width - 2*padding, curve_amount, char_spacing,
text_color=text_color)
else:
# Calculate total line width including character spacing
total_width = 0
for i, char in enumerate(line):
bbox = loaded_font.getbbox(char)
char_width = bbox[2] - bbox[0]
total_width += char_width
if i < len(line) - 1: # Add spacing between chars, but not after last char
total_width += loaded_font.size * char_spacing
if horizontal_pos == "left":
x = padding + position_offset
else: # center
# Center based on actual total width including spacing
x = (width - total_width) // 2
# Calculate x position based on justification
if random_position:
x += random.randint(-10, 10)
# For random positioning, use the existing logic but adjust for justification
if horizontal_pos == "left":
base_x = padding + position_offset
else: # center
base_x = self.calculate_line_x_position(line, loaded_font, "center", width, padding, char_spacing)
x = base_x + random.randint(-10, 10)
else:
# Use text justification parameter
x = self.calculate_line_x_position(line, loaded_font, text_justification, width, padding, char_spacing)
self.draw_text_with_border(draw, line, loaded_font, x, y, char_spacing, text_color, shadow_offset, outline_thickness, use_gradient, gradient_direction, background_style, background_color, background_padding)
y += line_height