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()
|