You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python的Wand库实现图像中文本字号统一、边框盒子自适应大小

修复方案

问题原因说明

  1. 盒子重叠/内容丢失:原代码中y_offset直接赋值为当前盒子高度,没有累加偏移量,导致后续盒子覆盖之前的内容,看起来像空白和丢失盒子
  2. 边框宽度不一致:Wand的描边是沿路径居中绘制的,原矩形坐标没有考虑描边宽度,导致部分边框被画布边缘裁切
  3. 文本位置异常:没有基于字体度量的垂直参数调整文本坐标,导致文本偏移挤压内边距

修改后的完整代码

import re
from unicodedata import normalize

from docx import Document
from wand.image import Image
from wand.drawing import Drawing
from wand.font import Font

# 全局配置
FONT_PATH = '/System/Library/Fonts/timesnewroman.ttf'
FONT_SIZE = 12
BOX_MAX_WIDTH = 396
BORDER_WIDTH = 2
X_PADDING = 10
Y_PADDING = 10
BOX_SPACING = 10

doc = Document("P.docx")
docText = []
for para in doc.paragraphs:
    docText.append(para.text)
fullText = "\n".join(docText)

ct = 242

def capitalize(str_list):
    # 改为返回列表,避免生成器迭代一次失效
    return [s[0].upper() + s[1:] if s else s for s in str_list]

def find_matches(text):
    return capitalize(
        [
            m
            for m in re.findall(
                r"^[^0-9]\s+([^.;]+\s*)+[.;]+", normalize("NFKD", text), re.MULTILINE
            )
        ]
    )

def to_chunks(words, size):
    for idx in range(0, len(words), size):
        yield words[idx:idx + size]

def rebuild_text(words, size, serial_num):
    # 把编号单独作为最后一行
    lines = [" ".join(w) for w in to_chunks(words, size)]
    lines.append(str(serial_num))
    return "\n".join(lines)

# 初始化画布,预留足够尺寸
with Image(width=500, height=3000, pseudo='xc:white') as img:
    current_y = Y_PADDING
    all_matches = list(find_matches(text=fullText))
    for match in all_matches:
        ct += 1
        with Drawing() as ctx:
            # 配置全局字体样式
            ctx.font = FONT_PATH
            ctx.font_size = FONT_SIZE
            ctx.text_alignment = 'center'
            ctx.fill_color = 'black'
            
            # 计算文本换行
            words = match.split(" ")
            word_count = len(words)
            text = ""
            text_height = 0
            max_text_width = BOX_MAX_WIDTH - 2*X_PADDING
            while word_count > 0:
                temp_text = rebuild_text(words, word_count, ct)
                metrics = ctx.get_font_metrics(img, temp_text, multiline=True)
                if metrics.text_width <= max_text_width:
                    text = temp_text
                    text_height = metrics.text_height
                    break
                word_count -= 1
            
            # 计算盒子尺寸
            box_width = BOX_MAX_WIDTH
            box_height = text_height + 2*Y_PADDING
            
            # 绘制边框和背景
            ctx.fill_color = 'white'
            ctx.stroke_color = 'black'
            ctx.stroke_width = BORDER_WIDTH
            ctx.rectangle(
                left=X_PADDING, 
                top=current_y, 
                width=box_width, 
                height=box_height
            )
            
            # 绘制文本,垂直居中对齐
            text_x = X_PADDING + box_width//2
            text_y = current_y + Y_PADDING + int(ctx.get_font_metrics(img, "啊").ascender)
            ctx.text(text_x, text_y, text)
            
            # 渲染当前内容
            ctx.draw(img)
            
            # 更新下一个盒子的偏移量
            current_y += box_height + BOX_SPACING
    
    # 裁切空白区域
    img.crop(right=X_PADDING + BOX_MAX_WIDTH + X_PADDING, bottom=current_y - BOX_SPACING)
    img.save(filename='patdrawdemoTest.png')

核心修改点

  • 修复了垂直偏移量的累加逻辑,不会再出现盒子重叠
  • 调整了边框绘制坐标,所有边框宽度统一
  • 基于字体度量调整文本垂直位置,保证文本居中
  • 显式设置字号为12号,统一所有文本样式
  • 修复了原匹配结果生成器只能迭代一次的问题,不会丢失匹配项
  • 单独处理编号行的换行逻辑,不会出现异常断行

内容的提问来源于stack exchange,提问作者Jimmy Wede

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.15 13:45:07