fix(ppt): 表格行高按内容估算逐行设置——显式换行强制断行+CJK/ASCII显示宽度估算换行数+行距余量1.45;根治实测长文本(含换行项目符号列表)在行底被截断;总高超页截行加提示不跨页拆表
This commit is contained in:
parent
47f191fc81
commit
09d77f7483
@ -233,33 +233,65 @@ def _content_header(slide, title, kicker=""):
|
||||
|
||||
# ══════════════════════ 表格渲染(真 Table,非文本) ══════════════════════
|
||||
|
||||
def _disp_w(s):
|
||||
"""显示宽度(CJK=1.0、ASCII≈0.55 字符单位),用于估算换行数。"""
|
||||
return sum(1.0 if ord(ch) > 0x2E7F else 0.55 for ch in (s or ""))
|
||||
|
||||
|
||||
def _add_table(slide, rows, top, width=None):
|
||||
"""渲染 markdown 表格为 pptx Table;返回占用高度(英寸)。超宽自动缩字号。"""
|
||||
"""渲染 markdown 表格为 pptx Table;返回占用高度(英寸)。超宽自动缩字号。
|
||||
|
||||
行高按内容估算逐行设置(2026-09-11 实测教训:统一行高时 LibreOffice/WPS
|
||||
不按换行撑行 → 长文本在行底被截断)。总高超页则截行加提示(不跨页拆表,
|
||||
保持单表完整可读)。
|
||||
"""
|
||||
from pptx.util import Inches, Pt
|
||||
import math
|
||||
if not rows or not rows[0]:
|
||||
return 0.0
|
||||
width = width or (SW - 2 * MARGIN_X)
|
||||
ncol = max(len(r) for r in rows)
|
||||
rows = [r + [""] * (ncol - len(r)) for r in rows]
|
||||
nrow = len(rows)
|
||||
row_h = 0.36
|
||||
avail = SH - 0.95 - top
|
||||
max_rows = max(2, int(avail / row_h))
|
||||
if nrow > max_rows:
|
||||
rows = rows[:max_rows - 1] + [["… 其余 %d 行见平台详情页" % (nrow - max_rows + 1)] + [""] * (ncol - 1)]
|
||||
nrow = len(rows)
|
||||
total_h = min(row_h * nrow, avail)
|
||||
gf = slide.shapes.add_table(nrow, ncol, Inches(MARGIN_X), Inches(top),
|
||||
Inches(width), Inches(total_h))
|
||||
tbl = gf.table
|
||||
# 列宽:首列略宽(多为名称),其余均分
|
||||
first = min(width * 0.30, 4.6)
|
||||
rest = (width - first) / max(1, ncol - 1)
|
||||
for ci in range(ncol):
|
||||
tbl.columns[ci].width = Inches(first if ci == 0 else rest)
|
||||
col_w = [first if ci == 0 else rest for ci in range(ncol)]
|
||||
fs = 11 if ncol <= 5 else (10 if ncol <= 7 else 9)
|
||||
line_h = fs * 1.45 / 72.0 # 每行文本高度(英寸,含行距余量)
|
||||
pad = 0.12 # 单元格上下边距+余量
|
||||
|
||||
def row_height(row_vals):
|
||||
lines = 1
|
||||
for ci, val in enumerate(row_vals):
|
||||
cap = max(2.0, (col_w[ci] - 0.16) * 72.0 / fs) # 每行可容纳字符单位
|
||||
n = 0
|
||||
for seg in (val or "")[:180].split("\n"): # 显式换行强制断行
|
||||
n += max(1, math.ceil(_disp_w(seg) / cap))
|
||||
lines = max(lines, n)
|
||||
return lines * line_h + pad
|
||||
|
||||
heights = [row_height(r) for r in rows]
|
||||
total_h = sum(heights)
|
||||
if total_h > avail:
|
||||
# 从尾行往前砍到放得下(至少留表头+1 数据行+提示行)
|
||||
keep = nrow
|
||||
while keep > 3 and sum(heights[:keep]) + row_height(
|
||||
["…"] + [""] * (ncol - 1)) > avail:
|
||||
keep -= 1
|
||||
cut = nrow - keep
|
||||
rows = rows[:keep] + [["… 其余 %d 行见平台详情页" % cut] + [""] * (ncol - 1)]
|
||||
heights = heights[:keep] + [row_height(rows[-1])]
|
||||
nrow = len(rows)
|
||||
total_h = sum(heights)
|
||||
gf = slide.shapes.add_table(nrow, ncol, Inches(MARGIN_X), Inches(top),
|
||||
Inches(width), Inches(total_h))
|
||||
tbl = gf.table
|
||||
for ci in range(ncol):
|
||||
tbl.columns[ci].width = Inches(col_w[ci])
|
||||
for ri, row in enumerate(rows):
|
||||
tbl.rows[ri].height = Inches(total_h / nrow)
|
||||
tbl.rows[ri].height = Inches(heights[ri])
|
||||
for ci, val in enumerate(row):
|
||||
cell = tbl.cell(ri, ci)
|
||||
cell.margin_left = cell.margin_right = Inches(0.07)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user