101 lines
3.6 KiB
Python
101 lines
3.6 KiB
Python
"""检查 signed.docx 中签名图片对应的 drawing XML,看实际显示尺寸(EMU)。
|
||
|
||
目的:
|
||
字节已经证明一致(e2e_test.py),但用户仍看到"空白"。
|
||
本脚本读 document.xml,找到图片所在段落的:
|
||
- wp:extent (图片显示尺寸 EMU)
|
||
- 段落 pPr/spacing (line 行高)
|
||
- 段落所在表格单元格 tcW (单元格宽度)
|
||
对比图片实际像素 vs 显示尺寸 vs 段落行高,定位空白的视觉来源。
|
||
|
||
EMU 换算:
|
||
1 inch = 914400 EMU = 2.54 cm
|
||
1 cm = 360000 EMU
|
||
"""
|
||
import sys
|
||
import zipfile
|
||
import xml.etree.ElementTree as ET
|
||
from pathlib import Path
|
||
|
||
BASE_DIR = Path(__file__).resolve().parent
|
||
DOCX_PATH = BASE_DIR / "output" / "e2e" / "signed.docx"
|
||
|
||
W = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
|
||
WP = 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing'
|
||
A = 'http://schemas.openxmlformats.org/drawingml/2006/main'
|
||
|
||
|
||
def emu_to_cm(emu):
|
||
try:
|
||
return int(emu) / 360000.0
|
||
except (TypeError, ValueError):
|
||
return None
|
||
|
||
|
||
def main():
|
||
if not DOCX_PATH.exists():
|
||
print(f"docx 不存在: {DOCX_PATH}")
|
||
print("请先运行: python image_test/e2e_test.py")
|
||
sys.exit(1)
|
||
|
||
with zipfile.ZipFile(DOCX_PATH) as z:
|
||
xml_bytes = z.read('word/document.xml')
|
||
|
||
root = ET.fromstring(xml_bytes)
|
||
|
||
print("=== 段落列表(含 spacing / drawing)===\n")
|
||
for i, p in enumerate(root.iter(f'{{{W}}}p'), 1):
|
||
pPr = p.find(f'{{{W}}}pPr')
|
||
spacing_info = ""
|
||
if pPr is not None:
|
||
sp = pPr.find(f'{{{W}}}spacing')
|
||
if sp is not None:
|
||
line = sp.get(f'{{{W}}}line')
|
||
rule = sp.get(f'{{{W}}}lineRule')
|
||
before = sp.get(f'{{{W}}}before')
|
||
after = sp.get(f'{{{W}}}after')
|
||
if line or before or after:
|
||
parts = []
|
||
if line:
|
||
cm = emu_to_cm(int(line) / 20 * 12700) if rule == 'exact' else None
|
||
parts.append(f"line={line}({rule})->{cm:.2f}cm" if cm else f"line={line}({rule})")
|
||
if before:
|
||
parts.append(f"before={before}({int(before)/20:.1f}pt)")
|
||
if after:
|
||
parts.append(f"after={after}({int(after)/20:.1f}pt)")
|
||
spacing_info = " | ".join(parts)
|
||
|
||
text = "".join(t.text or '' for t in p.iter(f'{{{W}}}t'))
|
||
has_drawing = len(list(p.iter(f'{{{WP}}}inline'))) > 0
|
||
marker = "[图]" if has_drawing else " "
|
||
|
||
print(f"[段{i:02d}] {marker} text={text[:40]!r}")
|
||
if spacing_info:
|
||
print(f" spacing: {spacing_info}")
|
||
|
||
for inline in p.iter(f'{{{WP}}}inline'):
|
||
extent = inline.find(f'{{{WP}}}extent')
|
||
if extent is not None:
|
||
cx = extent.get('cx')
|
||
cy = extent.get('cy')
|
||
print(f" drawing extent: cx={cx}({emu_to_cm(cx):.2f}cm) cy={cy}({emu_to_cm(cy):.2f}cm)")
|
||
|
||
tc_parent = None
|
||
parent = p
|
||
while parent is not None:
|
||
parent_iter = list(root.iter())
|
||
break
|
||
for tc in root.iter(f'{{{W}}}tc'):
|
||
if p in list(tc.iter(f'{{{W}}}p')):
|
||
tcPr = tc.find(f'{{{W}}}tcPr')
|
||
if tcPr is not None:
|
||
tcW = tcPr.find(f'{{{W}}}tcW')
|
||
if tcW is not None:
|
||
w = tcW.get(f'{{{W}}}w')
|
||
t = tcW.get(f'{{{W}}}type')
|
||
print(f" cell width: w={w} type={t}")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
main()
|