Files
SignatureSystem/lib/__pycache__/field_codes.cpython-39.pyc

356 lines
27 KiB
Plaintext
Raw Normal View History

2026-07-20 13:16:17 +08:00
a
2026-07-20 16:43:08 +08:00
<13>]j <0A><00>@srdZddlZddlZddlmZddlmZddlmZddl m
2026-07-20 13:16:17 +08:00
Z
m Z e<02> e <0A>Ze<01>dej<10>Ze<01>d<08>Zd Zd
2026-07-20 16:43:08 +08:00
Zd Zd Zd Zdd<0F>Zdd<11>Zdd<13>Zdd<15>ZdKdd<18>Zdd<1A>Zdd<1C>Zdd<1E>ZdZ d d!<21>Z!dLd"d#<23>Z"dMd&d'<27>Z#d(Z$d)d*<2A>Z%d+d,<2C>Z&d-d.<2E>Z'd/d0<64>Z(d1d2<64>Z)d3d4<64>Z*d5d6<64>Z+d7d8<64>Z,d9d:<3A>Z-d;Z.dNd=d><3E>Z/d?d@<40>Z0dAdB<64>Z1dCdD<64>Z2dOdEdF<64>Z3dPdGdH<64>Z4dIdJ<64>Z5dS)QuDWord SET 域代码处理工具。
2026-07-20 13:16:17 +08:00
支持两类域代码定位:
1. ``w:fldSimple`` 简单域,``w:instr`` 属性内含 ``SET USER_xxx``。
2. ``w:instrText`` + ``w:fldChar`` 组合的复杂域。
提供:
- :func:`scan_user_markers` 扫描文档中所有 ``SET USER_<name>`` 标记。
- :func:`insert_image_after_field` 在指定标记的域之后插入签名图片。
- :func:`build_set_field_xml` 构造一段 ``SET USER_<name> \* MERGEFORMAT`` 域代码 XML。
- :func:`replace_text_with_set_field` 将段落中命中的可见文本替换为 SET 域 + 宽度补偿空格。
<EFBFBD>N)<01>deepcopy)<01>qn<71><01> OxmlElement)<02>Cm<43>Emuz\bSET\s+USER_(\S+)z^USER_(\S+?)\s*(?:\\.*)?$z<http://schemas.openxmlformats.org/wordprocessingml/2006/mainzFhttp://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawingz5http://schemas.openxmlformats.org/drawingml/2006/mainz8http://schemas.openxmlformats.org/drawingml/2006/picturezChttp://schemas.openxmlformats.org/officeDocument/2006/relationshipscCsB|dur dS|<00><00>}|<01>d<03>r*|dd<01>}|<01>dd<06>d}|<01><00>S)u<>去除 ``\* MERGEFORMAT`` 等 Word 开关,并 trim 末尾空白。
输入示例:
- ``USER_zhangsan`` -> ``zhangsan``
- ``USER_zhangsan \* MERGEFORMAT`` -> ``zhangsan``
N<><00>USER_<52><00>\<5C>r)<03>strip<69>
startswith<EFBFBD>split)<02>nameZtxt<78>r<00>`E:\projects\wxwx\projects_job\GeneralRequirement\SignatureSystem\BackEnd\Code\lib\field_codes.py<70>_strip_switches!s
 rc cs<>|jj}|j}|<01>td<01><01>D]}||fVq|jD]<5D>}|j|j|jfD]2}|durF|j}|j <09>td<01><01>D]}||fVqhqF|j
|j |j fD]2}|dur<>|j}|j <09>td<01><01>D]}||fVq<>q<EFBFBD>q2dS)u<>递归遍历文档中所有段落:正文、表格(含嵌套)、文本框、页眉页脚。
返回 ``(paragraph_element, part)`` 元组,``part`` 用于 ``add_picture`` 关系绑定。
zw:pN) <0A>element<6E>body<64>part<72>iterr<00>sections<6E>header<65>first_page_header<65>even_page_header<65>_element<6E>footer<65>first_page_footer<65>even_page_footer) <09>docr<00>doc_part<72>para<72>section<6F>hdrZhdr_part<72>ftrZftr_partrrr<00>_iter_all_paragraphs1s 
r&ccst|<00>D]\}}|VqdS)u)仅迭代段落元素(不带 part。N)r&)r r"<00>_partrrr<00>_iter_all_paragraph_elementsHsr(ccs<>|jj}|<01>td<01><01>D]
}|Vq|jD]x}|j|j|jfD](}|dur<|j<08>td<01><01>D]
}|VqXq<|j |j
|j fD](}|durv|j<08>td<01><01>D]
}|Vq<>qvq(dS)uW递归遍历所有表格包括正文、文本框、页眉页脚中的嵌套表格。zw:tblN) rrrrrrrrrrrr)r r<00>tblr#r$r%rrr<00>_iter_all_tablesNs

r*<00> 前端操作c CsNg}t<00>}t|<00>D<00>]\}}|<04><02>D<00>]}|j}|td<01>kr<>|<06>td<02><01>pLd}t<06>|<08>} | r<>t| <09> d<04><01>}
|
spq$|
<EFBFBD>
<EFBFBD>} | |vr<>q$|<03> | <0B>|<02> d|
<EFBFBD><00>|
dd<07><03>q$|td<08>kr$|j d ur<>d<03>|<06><0F><00>n|j } | s<>q$t<06>| <0C>} | r$t| <09> d<04><01>}
|
s<EFBFBD>q$|
<EFBFBD>
<EFBFBD>} | |v<00>r q$|<03> | <0B>|<02> d|
<EFBFBD><00>|
d
d<07><03>q$qtjd t|<02>d |id <0A>|S)ud扫描文档中所有 ``SET USER_<name>`` 标记,返回去重后的 ``[{marker, name}]`` 列表。<E8A1A8> w:fldSimple<6C>w:instrrr r <00>simple)<03>markerr<00>kind<6E> w:instrTextN<74>complexu1[auto扫描] 发现 %d 个 SET USER_<name> 标记<E6A087>
log_source<EFBFBD><01>extra)<13>setr&r<00>tagr<00>get<65>USER_MARKER_RE<52>searchr<00>group<75>lower<65>add<64>append<6E>text<78>join<69>itertext<78>logger<65>info<66>len) r r3<00>foundZ seen_keysr"r'<00>elemr7<00>instr<74>mr<00>keyr?rrr<00>scan_user_markers_sF 

 


 <06>rJcCs|}|<02>d<01>sd|<02><00>}n|}|<03>d<01>r4|dd<03>n|}|<04><01>}g}t|<00>D]<5D>\}}|<07><03>D]<5D>} | j}
|
td<04>kr<>| <09>td<05><01>p<>d} t<07>| <0B>} | r<>t | <0C>
d<07><01>} | <0A><01>|kr<>|<06> d|| |f<04>q\|
td <09>kr\| j p<>d}t<07>|<0E>} | r\t | <0C>
d<07><01>} | <0A><01>|kr\|<06> d
|| |f<04>q\qL|S) u<>根据 marker 名称(如 ``USER_zhangsan`` 或 ``zhangsan``)查找域代码宿主元素。
返回 ``(kind, parent_paragraph, fld_element_or_run, part)`` 列表,供插入图片使用。
r r
Nr,r-rr r.r1r2) rr<r&rr7rr8r9r:rr;r>r?)r <00> marker_name<6D>targetZ target_full<6C> target_nameZ target_lc<6C>resultsr"rrFr7rGrHrr?rrr<00>_find_field_elements_for_marker<65>s2
   
  

 rOcCs td<01>}|S)u<>构造一个包含图片 drawing 的 ``<w:r>`` 元素。
保留作为低层 helper正式插入使用 :func:`_insert_picture_into_paragraph_after_anchor`
通过 python-docx 的 ``Run.add_picture`` 完成关系绑定与 drawing XML。
<20>w:rr)<04>
image_path<EFBFBD>width_cm<63> height_cm<63>runrrr<00>_build_run_with_drawing<6E>srUc
CsNddlm}ddlm}|<00><04>}|||<07>}|<08><05>} | j|||<03>||<04>d<04>| S)u<>在指定段落元素的末尾添加一个图片 run。
使用 python-docx 的 Paragraph/Run 高层 API避免手动维护 rels。
r<00><01> Paragraph<70>r<00><02>width<74>height)<07>docx.text.paragraphrW<00> docx.sharedr<00> getparent<6E>add_run<75> add_picture)
<EFBFBD>paragraph_elementr!rQrRrSrW<00>_Cm<43>parent<6E>para_obj<62>run_objrrr<00>_add_picture_run_to_paragraph<70>s  
2026-07-20 16:43:08 +08:00
rf<00><>1c
Cs`t|<00>p
d}|<00>td<02><01>}d}|dur<>|<03>td<05><01>}|dur<>|<05>td<06><01>pZ|<05>td<07><01>pZd}z t|<06>}Wnttfy~Yn0t|d <00>}|<00><07>D<00>]<5D>}||ur<><00>q\|jtd
<EFBFBD>kr<>q<EFBFBD>|j p<>d } | s<>q<EFBFBD>|<08>
<EFBFBD>}
|
dur<>|
jtd <0C>kr<>|
<EFBFBD>
<EFBFBD>}
q<EFBFBD>|} |
du<01>r<>|
<EFBFBD>td <0A><01>} | du<01>r<>| <0C>td<0E><01>} | du<01>r<>| <0A>td<0F><01><01>r<>z:t| <0A>td<0F><01><01>d}d|k<01>rndk<01>rvnn|} Wnttf<02>y<>Yn0| D]<5D>}t |<0F>}d|k<01>r<>dk<01>s nd|k<01>r<>dk<01>s nd|k<01>r<>dk<01>s nd|k<01>rdk<01>r"nn|t| dt <00>7}n4|<0F> <0A><00>rB|t| dt <00>7}n|t| dt <00>7}<07>q<>q<EFBFBD>|S)u<>估算段落起点到 target_elem 之间所有可见文字的水平宽度EMU
用于浮动图片anchor 模式)相对段落起点的水平 posOffset 估算,
让图片左边缘接近 SET 域在段落中的位置。
算法:
- 遍历段落所有后代,遇到 target_elem 停止;
- 对每个 <w:t> 文本,按所在 run 的字号(无字号用段落默认)估算字符宽度:
- CJK / 全角 / 中文标点1.0 × 字号 pt
- 空格0.5 × 字号 pt
- 其他 ASCII0.55 × 字号 pt
- 加段落左缩进(<w:ind w:left>twips → EMU
返回 EMU 整数。Word 实际渲染会有偏差(字体度量、字距差异),但通常 ±10% 内。
<20>%@<40>w:pPrrN<>w:ind<6E>w:left<66>w:start<72>0g؃@<40>w:trrP<00>w:rPr<50>w:sz<73>w:val<61>@<40>@<40>X@iNi<><69>i0i?0i<00>i<><69>i4i<>M<00><00>?<3F><00>?g<><67><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?)<0E>_detect_paragraph_font_size_pt<70>findrr8<00>int<6E>
ValueError<EFBFBD> TypeErrorrr7r?r^<00>ord<72> _PT_PER_EMU<4D>isspace)<11>para_xmlZ target_elem<65>
default_pt<EFBFBD>pPrZ
left_twips<EFBFBD>indZleft_strZ total_emurFr?rT<00>font_pt<70>rPr<50>sz<73>pt<70>ch<63>coderrr<00>_estimate_text_width_emu_before<72>sn    



<04>
<02>
<04>
<02>
<04>
<02> 
r<>c CsDtd<01>|_|<00>dd<03>|<00>dd<05>|<00>dd<03>|<00>dd<03>|<00>dd <09>|<00>d
d <09>|<00>td <0B><01>}td <0C>}|<03>d d<03>|<03>dd<03>td<0F>}|<04>dd<11>td<12>}tt|<01><01>|_|<04>|<05>td<13>}|<06>dd<11>td<12>}d|_|<06>|<07>|||fD]}|<02> |<08>q<>td<14>} dD]}
| <09>td|
<EFBFBD><00><02>d<03><00>q|<02>
| <09>td<17>} | <09>
| <0B>dS)u<>把 python-docx 生成的 <wp:inline> 原地改造成 <wp:anchor>。
固定参数behindDoc=0盖在文字前/ wrapNone不环绕/ 水平 paragraph 模式
+ posOffset=h_offset_emu默认 0=贴段落起点;可传入 SET 域估算偏移让图片对齐域位置)
/ 垂直 paragraph + posOffset=0贴段落顶部
保留 <a:graphic> 子树(含 r:embed
z wp:anchorZ simplePosrmZrelativeHeightZ25<32> behindDoc<6F>lockedZ layoutInCell<6C>1<> allowOverlapz wp:extentz wp:simplePos<6F>x<>yz wp:positionH<6E> relativeFrom<6F> paragraphz wp:posOffsetz wp:positionVzwp:effectExtent)<04>l<>t<>r<>bzwp:z wp:wrapNoneN) rr7r6rxr<00>strryr?r><00> addprevious<75>addnext) Z inline_elem<65> h_offset_emu<6D>extentZ
simple_pos<EFBFBD>pos_hZh_off<66>pos_vZv_offrFZeffect<63>kZ wrap_nonerrr<00>_convert_inline_to_anchors:
         
 
 
r<>T<>inlinec* Cs<>|}|<08>d<01>r|dd<03>}t|<08>}t||<08>} | sHtjd|d|id<06>dSt|<00>}
d} ddlm} dd lm } | D<00>]J\}}}}<11>z<>|d
kr<>|}nt
||<10>}|dur<>|n|}|}|}|r<>|dk<01>r<>t |<0F>}t |<0F>}|dur<>|
}tj d |r<>|<17>nd d|id<06>t||<17>}z^dd lm}|<18>|<02><01>}|j\}}Wd<00>n1<00>s>0Y|<1B>r`|<14>r`|||}Wnt<13>yzd}Yn0tj d|<17>r<>|<17>nd |d|id<06>|dk<02>r<>|<06>r<>|<14>r<>|dk<04>r<>t|||<05>t|||<05>t |<0F>}t|<0F>}t|<0F>\}}|<1D>r|<1E>s|<1F>rtd|||<00>}t||||<1C>\} }!| |!f||fk<03>r<>tj d|<15>pHd|<14>pPd| <20>pXd|!<21>p`d|<1C>rr|d<12>d<13>nd |<1D>r<>|d<12>d<13>nd ||d|id<06>
n,||} }!tj d| <20>p<>d|!<21>p<>dd|id<06>|<0F><1A>}"| ||<11>}#|#<23><1B>}$|$j|| | <20>| |!<21>d<15>|dk<02>rJ|$j<1D>td<17><01>}%|%du<01>rJ|%<25>td<18><01>}&|&du<01>rJt ||<12>}'t!|&|'d<19>|$j}(|<12>"|(<28>| d7} tj d||d|id<06>Wqpt<13>y<>})z&tj#d|t$|)<29>d|id<06>WYd})~)qpd})~)00qp| S)u<>在指定 ``USER_<name>`` 域之后插入签名图片。
2026-07-20 13:16:17 +08:00
返回插入的图片数量。
2026-07-20 16:43:08 +08:00
``mode``:
- ``'inline'``(默认):嵌入式,受段落行高约束,需 ``relax_layout`` 放宽 + 缩放兜底
- ``'anchor'``:浮动覆盖,跳过 relax_layout 与缩放兜底,图片浮在文字上方
2026-07-20 13:16:17 +08:00
``relax_layout=True``(默认)时,若标记段落或所在表格行有固定高度约束
``lineRule="exact"`` / ``hRule="exact"``)小于签名需要的高度,
自动把约束放宽为 ``atLeast`` 并上调数值,避免 Word 硬裁剪图片。
设为 ``False`` 时严格保留原文档布局,仅靠等比例缩放把图片压进约束内。
2026-07-20 16:43:08 +08:00
仅在 ``mode='inline'`` 时生效,``anchor`` 模式跳过。
2026-07-20 13:16:17 +08:00
r r
2026-07-20 16:43:08 +08:00
Nu,[Word处理] 未找到标记 %s 的域代码r3r4rrVrXr.uB[Word处理] 段落无显式字号,使用文档默认字号 %sptu<><01>Imageu@[Word处理] 自动尺寸: 字号=%spt → 推算 height=%.2f cmr<6D>rvu<>[Word处理] 图片缩放以适配插入位置: %.2fx%.2f cm -> %.2fx%.2f cm (段落行高上限=%s, 单元格宽上限=%s, 左右缩进=%.2f+%.2fcm)z.2f<EFBFBD>cmu|[Word处理] 浮动覆盖模式anchor: 使用图片原始尺寸 %.2fx%.2f cm跳过段落约束放宽与缩放兜底rY<00>anchor<6F> w:drawing<6E> wp:inline)r<>r u6[Word处理] 在 %s 域后插入图片成功 (mode=%s)u&[Word处理] 插入图片失败 %s: %s)%rrrOrBZwarning<6E>_get_doc_default_font_size_ptr\rWr]r<00>_find_complex_field_end<6E>_detect_paragraph_max_height_cmrwrC<00>_auto_size_height_cmZPILr<4C><00>open<65>size<7A> Exception<6F>_relax_paragraph_for_image<67>_relax_table_row_for_image<67>_detect_table_cell_max_width_cm<63>_detect_paragraph_indent_cm<63>max<61> _scale_to_fitr^r_r`rrxrr<>r<>r<><00>errorr<72>)*r r/rQrRrSr3<00> relax_layout<75>moderM<00>matchesZdoc_default_ptZinsertedrWrbr0rZ host_elemr<00> anchor_elemZend_elemZcur_hZcur_wZmax_h_cm_initialr<6C>Z _PILImageZ_imZ_iwZ_ih<69>max_h_cm<63>max_w_cmZleft_cmZright_cmZfinal_wZfinal_hZ
parent_xmlrdre<00>drawingr<67>r<>Z new_run_xml<6D>errr<00>insert_image_after_fieldDs<>
2026-07-20 13:16:17 +08:00
 
  
 <06>
  * 
2026-07-20 16:43:08 +08:00
<08>
    <06>
<06>




 

<06>
2r<>g<>v<EFBFBD><76>n<EFBFBD><6E>@c Cs<>|<00>td<01><01>}|durdS|<01>td<03><01>}|dur4dS|<02>td<04><01>}|<02>td<05><01>}|r\|dkr`dSzt|<03>tWSttfy<>YdS0dS)u从段落属性读取固定行高lineRule="exact"),返回 cm 或 None。
2026-07-20 13:16:17 +08:00
Word 段落 ``<w:pPr><w:spacing w:line="..." w:lineRule="exact"/></w:pPr>`` 中,
line 字段在 exact 模式下单位为 1/20 pt (twips)。其他模式auto/atLeast不视为硬约束。
2026-07-20 16:43:08 +08:00
riN<> w:spacing<6E>w:line<6E>
w:lineRule<6C>exact<63>rxrr8ry<00> _TWIPS_PER_CMrzr{)rr<><00>spacing<6E>line<6E> line_rulerrrr<><00>s r<>c Cs<>|<00><00>}|dur(|jtd<02>kr(|<01><00>}q|dus>|jtd<02>krBdS|<01>td<04><01>}|dur\dS|<02>td<05><01>}|durvdS|<03>td<06><01>}|<03>td<07><01>p<>d}|s<>d|fSzt|<04>t}Wnttfy<>d|fYS0||fS) uQ读取段落所在表格行的固定高度约束。
2026-07-20 13:16:17 +08:00
向上查找最近的 ``<w:tr>``,读 ``<w:trPr><w:trHeight w:val w:hRule/></w:trPr>``。
返回 ``(val_cm, rule)`` —— ``rule ∈ {'exact', 'atLeast', None}``。
按 OOXML 规范,``hRule`` 缺省为 ``atLeast``(不裁剪),仅 ``exact`` 视为硬约束。
N<>w:tr)NN<4E>w:trPr<50>
2026-07-20 16:43:08 +08:00
w:trHeightrq<00>w:hRule<6C>atLeast<73> r^r7rrxr8ryr<>rzr{)rrc<00>trPr<50>trHeight<68>val<61>rule<6C>val_cmrrr<00>_detect_table_row_height_rule<6C>s(
r<>c Cs<>|<00>td<01><01>}|durdS|<01>td<04><01>}|dur4dS|<02>td<05><01>pR|<02>td<06><01>pRd}|<02>td<08><01>pr|<02>td <09><01>prd}zt|<03>tt|<04>tfWSttfy<>YdS0dS)
uT读段落左右缩进 ``<w:ind w:left w:right>``,返回 ``(left_cm, right_cm)``。riN)<02>r<>rjrkrlrmzw:rightzw:endr<64>)rr<>r<><00>left<66>rightrrrr<><00>s  r<>c Cs|ddl}|r|dkrdSt|<03>|t<00><01>}|<00>td<04><01>}|durTtd<04>}|<00>d|<05>|<05>td<05><01>}|dur<>td<05>}|<06>td<06>t |<04><01>|<06>td<07>d<08>|<05>
2026-07-20 13:16:17 +08:00
|<06>t j d ||t|d
|id <0B>d S|<06> td<06><01>}|<06> td<07><01>p<>d }|<07>r&z&t|<07>} |dv<00>r
| |k<05>r
WdSWnttf<02>y$Yn0|}
|<07>p2d } |<06>td<07>d<08>|<06>td<06>t |<04><01>t j d|
| ||t|d
|id <0B>d S)uI确保段落行高至少能容纳签名高度,避免 inline 图片被行盒子裁切。
inline 图片默认骑在文字 baseline 上方,所需高度 = 图片高度。
若段落行高(由 spacing/@line + lineRule 决定,或基于字号默认推算)小于该值,
baseline 上方会溢出并被 Word 裁掉(典型表现:图片上方被切)。
处理所有 spacing 状态:
- 无 pPr / 无 spacing → 主动创建 ``spacing line=needed atLeast``
- ``lineRule='exact'`` 且 line < needed → 改 ``atLeast`` + 上调 line
- ``lineRule='atLeast'`` 且 line < needed → 上调 line
- ``lineRule='auto'`` / 缺省 → 改 ``atLeast`` + 设 line = needed
(默认行高基于字号,对 inline 图片通常不够)
- 已 ≥ needed → no-op
返回是否发生过改动。
2026-07-20 16:43:08 +08:00
rNFrir<>r<>r<>r<>uh[Word处理] 段落无显式行高,新增 spacing atLeast/%d twips (%.2fcm) 以容纳 %.2fcm 高签名r3r4T<>auto)r<>r<>un[Word处理] 放宽段落行高: lineRule %s→atLeast, line %s→%d twips (%.2fcm) 以容纳 %.2fcm 高签名)<10>mathry<00>ceilr<6C>rxrr<00>insertr6r<>r>rBrCr8rzr{) r<00> needed_h_cmr3r<><00> needed_twipsr<73>r<>r<>r<><00>
line_twipsZold_ruleZold_linerrrr<>sL  
2026-07-20 13:16:17 +08:00

<06>

2026-07-20 16:43:08 +08:00
<06>r<>c CsNddl}|r|dkrdS|<00><01>}|dur@|jtd<04>kr@|<04><01>}q |dusV|jtd<04>krZdS|<04>td<05><01>}|durtdS|<05>td<06><01>}|dur<>dS|<06>td<07><01>}|<06>td<08><01>p<>d }|d
2026-07-20 13:16:17 +08:00
ks<>|s<>dSzt|<07>t} Wntt fy<>YdS0| |kr<>dSt|<03>
|t<00><01>}
|<06> td<08>d <09>|<06> td<07>t |
<EFBFBD><01>t jd ||
| |
t|d |id <0A>dS)u)放宽表格行**硬高度约束**,避免图片被表格行盒子裁切。
OOXML 规范下表格行高规则:
- ``hRule='exact'``:行高**精确**为 val超过部分被裁切 ← 硬约束
- ``hRule='atLeast'`` 或缺省:行高**至少**为 val可自动撑高 ← 软约束,不裁切
因此本函数仅在 ``hRule='exact'`` 且 ``val < needed_h_cm`` 时介入:
- 改 ``hRule`` 为 ``atLeast`` + 上调 ``val`` 到 needed_h_cm
其他情况(无 trHeight、atLeast、已 ≥ needed、auto → no-op。
2026-07-20 16:43:08 +08:00
rNFr<46>r<>r<>rqr<>r<>r<>ux[Word处理] 放宽表格行高: hRule exact→atLeast, val %s→%d twips (%.2fcm → %.2fcm) 以容纳 %.2fcm 高签名r3r4T)r<>r^r7rrxr8ryr<>rzr{r<>r6r<>rBrC) rr<>r3r<>rcr<>r<>r<>r<>r<>r<>rrrr<>Ds@  
 <06>r<>c Cs<>|<00><00>}|dur(|jtd<02>kr(|<01><00>}q|dus>|jtd<02>krBdS|<01>td<03><01>}|dur\dS|<02>td<04><01>}|durvdS|<03>td<05><01>}|<03>td<06><01>}|r<>|dkr<>dSzt|<04>tWSttfy<>YdS0dS)uQ若段落位于表格单元格内,读取单元格宽度,返回 cm 或 None。Nzw:tczw:tcPrzw:tcWzw:wzw:type<70>pctr<74>)rrc<00>tcPr<50>tcWZw_valZw_typerrrr<>vs&
 r<>cCsX|r|s||fSd}|r.||kr.t|||<00>}|rH||krHt|||<00>}||||fS)uU等比例缩放图片,使其宽高都不超过约束。返回 (final_w, final_h)。ru)<01>min)rRrSr<>r<>Zratiorrrr<><00>s  r<>c
2026-07-20 13:16:17 +08:00
CsN|<00>td<01><01>D]<5D>}|<01>td<02><01>}|dur*q|<02>td<04><01>}|dur|<03>td<05><01>}|r<>z2t|<04>d}d|krtdkr<>nn
|WSWnttfy<>Yn0q<>q|<00>td <09><01>}|du<01>rJ|<06>td<02><01>}|du<01>rJ|<02>td<04><01>}|du<01>rJ|<03>td<05><01>}|<04>rJz2t|<04>d}d|k<01>r$dk<01>r.nn|WSWnttf<02>yHYn0dS)
u%读取段落字体大小point
优先级:
1. 段落第一个有 ``<w:rPr><w:sz>`` 的 run
2. 段落标记的 rPr``<w:pPr><w:rPr><w:sz>``
``w:sz`` 的 val 是 half-points如 21 = 10.5pt)。
找不到返回 None由调用方决定 fallback
2026-07-20 16:43:08 +08:00
rProNrprqrrrsrtri)rrrxr8ryrzr{)rr<>r<>r<>r<>r<>r<>rrrrw<00>s<
2026-07-20 13:16:17 +08:00
 


 
2026-07-20 16:43:08 +08:00
rwc
2026-07-20 13:16:17 +08:00
Cs<>z |jj}Wnty YdS0|<01>td<02><01>D]<5D>}|<02>td<03><01>dkrHq0|<02>td<05><01>}|dur<>|<03>td<06><01>}|dur<>|<04>td<07><01>}|r<>z2t|<05>d}d |kr<>d
kr<>nn
|WSWntt fy<>Yn0q<>q0|<01>td <0B><01>}|dur<>dS|<07>td <0C><01>}|du<00>r dS|<08>td<05><01>}|du<00>r(dS|<03>td<06><01>}|du<00>rDdS|<04>td<07><01>}|<05>s\dSz2t|<05>d}d |k<01>r<>d
k<01>r<>nn|WSWntt f<02>y<>YdS0dS) uE读文档级默认字号,按 Word 实际渲染优先级。
优先级:
1. ``Normal`` 样式(默认段落样式)的 ``<w:sz>`` —— Word 渲染段落时实际使用
2. ``<w:docDefaults><w:rPrDefault><w:rPr><w:sz>`` —— 全局兜底
找不到返回 None由调用方做最终 fallback
2026-07-20 16:43:08 +08:00
Nzw:stylez w:styleId<49>Normalrorprqrrrsrtz w:docDefaultsz w:rPrDefault)
<EFBFBD>stylesrr<>rrr8rxryrzr{) r Zstyles_element<6E>styler<65>r<>r<>r<><00> doc_defaultsZ rPr_defaultrrrr<><00>sT    
2026-07-20 13:16:17 +08:00


 
2026-07-20 16:43:08 +08:00
r<>ge<67><65><EFBFBD><EFBFBD><0F>?rrcCs||r6d|krdkr6nnttt|dd<04>d<05>d<06>S|rVd|krNdkrVnn|nd }|dt}ttt|d<04>d<05>d<06>S)
2026-07-20 13:16:17 +08:00
u9根据段落行高和字号自动推算签名高度cm
优先级:
1. 段落 exact line height → 用其 95%(留 5% 安全裕量,避免顶满行)
2. 字号 → 用 2 倍字号高度(中文文档常见签名比例:四号 14pt → 0.99cm
视觉自然不抢戏,又能看清笔画)
3. 字号缺失 → 用 10.5pt(中文 Word 通用默认)按 2 倍推算兜底,
避免 fallback 到 default_cm 导致过大
4. 默认 default_cm
范围约束:返回值 clamp 到 [0.5, 5.0] cm避免极端值。
2026-07-20 16:43:08 +08:00
g333333<33>?g$@gffffff<66>?rvg@<40>g@gR@rh)<04>roundr<64>r<><00>
_CM_PER_PT)Zline_height_cmr<6D>Z
default_cmZ effective_ptr<74>rrrr<>s
$ r<>cCsr|}d}|<00><00>D]\}||urq|jtd<02>kr|<04>td<03><01>}|dkrHd}q|dkr\|r\|S|dkr|SqdS)uB从 instrText 开始向后查找同段落的 fldChar end 元素。F<E38082> w:fldChar<61> w:fldCharType<70>separateT<65>endN)rr7rr8)r<>Zinstr_text_elem<65>currentZ found_seprFZftyperrrr<>s  
r<>c
2026-07-20 13:16:17 +08:00
Cs<><00>z<>ddl}ddlm}ddlm}ddlm}t|d<06><02>}|<07><08>}Wd<00>n1sX0Y|<04> |<08>} t
|j d<07>r<>|j <0B> | <09>nd}
|
dur<>|<05> | |j j<0E>}
|j <0B>|
|j<10>} n*d} |j j<11><12>D]} | j|
ur<>| j} q<>q<EFBFBD>|<01>td<08><01>} | dur<>WdS| <0A>td <09><01>}|du<00>rWdS|<0E>td
<EFBFBD><01>}|du<00>r4WdS|<0F>td <0B><01>}|du<00>rRWdS|<10>td <0C><01>}|du<00>rpWdStd <0A>}td<0E>}|<13>td<0F>| <0B>|<12>|<13>|<11>d|<12>Wn6t<1B>y<>}zt<1C>dt|<14><01>WYd}~n
2026-07-20 16:43:08 +08:00
d}~00dS)uE通过 python-docx 的 part 接口建立图片关系并补充 blip。rNr<4E>)<01> ImagePart)<01>RELATIONSHIP_TYPE<50>rb<72>related_part_lookup_by_imager<65>r<>z a:graphicz a:graphicDatazpic:picz pic:blipFillza:blipzr:embedu)[Word处理] 绑定图片关系失败: %s)<1F>osZdocx.image.imager<65><00>docx.parts.imager<65><00>docx.opc.constantsr<73>r<><00>read<61> from_blob<6F>hasattrrr<><00>
from_image<EFBFBD>package<67> relate_to<74>IMAGE<47>rels<6C>values<65> target_part<72>rIdrxrrr6r>r<>r<>rBr<>r<>)r Zrun_elemrQr<>r<>r<><00>RT<52>fZ
2026-07-20 13:16:17 +08:00
image_data<EFBFBD>image<67>
2026-07-20 16:43:08 +08:00
image_partr<EFBFBD><00>relr<6C>r<><00>graphicZ graphic_data<74>picZ blip_fill<6C>blipr<70>rrr<00>_attach_image_relationship&sN    &
2026-07-20 13:16:17 +08:00






2026-07-20 16:43:08 +08:00
rc Cs g}td<01>}td<02>}|<03>td<03>d<04>|<02>|<03>|<01>|<02>td<01>}td<05>}|<05>td<06>d<07>d|<00>d <09>|_|<04>|<05>|<01>|<04>td<01>}td<02>}|<07>td<03>d
2026-07-20 13:16:17 +08:00
<EFBFBD>|<06>|<07>|<01>|<06>td<01>}td <0B>} | <09>td<06>d<07>d | _|<08>| <09>|<01>|<08>td<01>}
td<02>} | <0B>td<03>d <0A>|
<EFBFBD>| <0B>|<01>|
<EFBFBD>|S)u<>构造一段复杂域 XML表示 ``SET USER_<name> \* MERGEFORMAT``。
返回三个 ``<w:r>`` 元素begin / instrText / separate / 空白 result / end。
合并为一个 list 返回,调用方依次追加到段落。
2026-07-20 16:43:08 +08:00
rPr<>r<>Zbeginr1<00> xml:space<63>preservez
SET USER_z \* MERGEFORMAT r<>rnrr<>)rr6rr>r?) r<00>runsZr1Z fld_beginZr2rGZr3Zfld_sepZr4Zt4Zr5Zfld_endrrr<00>build_set_field_runsTs:
2026-07-20 13:16:17 +08:00









2026-07-20 16:43:08 +08:00
r
cCs|pd}t|dd<00>S)u<>估算单字符宽度EMU用于补偿空格宽度。
2026-07-20 13:16:17 +08:00
Word 默认正文字号 10.5pt(小四),半角空格宽度约为字号的 0.25 倍。
2026-07-20 16:43:08 +08:00
rhg<00>?rg)ry)Z font_size_ptr<74>rrr<00>_approx_char_width_emusr c Cst|<00>td<01><01>}|t|<08>krdS||} | <09>td<03><01>}
2026-07-20 13:16:17 +08:00
|
dur@dS||k<02>r|
jpRd} | d|<02>} | |d<04>} t| <0C>}t| <0A>}t|<06>}|r<>tdt|<05><00>nd}| <09><07>}t|<12><01> | <09>}|<12>
| <09>|}|g||r<>|gng|gD]"}|dur<>q<EFBFBD>|<12> ||<15>|d7}q<>dSg}t ||d<00>D]>}||}|<18>td<03><01>}|<16> |du<01>rN|j<04>rN|jnd<05><00>q|d }|d|<02>} |d
}||d<04>} t| <0C>}t| <0A>}t|<06>}d<05>|<16>}||td d <0C>|dd
<EFBFBD>D<00><01>|<00>}|<07>r<>tdt|<1D><00>nd}|}|||d<00>}|d }t|<12><01> |<1F>}|D]}|<12>
|<18><00>q|}|g||<07>r:|gng|gD](}|du<00>rX<72>qF|<12> ||<15>|d7}<14>qFdS) u在段落 XML 中将指定 Run 区间内的字符替换为 SET 域 + 可选补偿空格。
参数:
- ``paragraph_element``: ``<w:p>`` lxml 元素。
- ``start_run_idx``/``end_run_idx``: 起止 ``<w:r>`` 在段落中的索引。
- ``start_offset``/``end_offset``: 起止 ``<w:t>`` 内字符偏移。
- ``captured_text``: 被替换的原始可见文本(用于估算补偿宽度)。
- ``name``: 用于 ``USER_<name>`` 的标识。
- ``compensate_spaces``: 是否插入普通空格补偿原宽度。
2026-07-20 16:43:08 +08:00
rPFrnNr<00> r Tr<00><><EFBFBD><EFBFBD><EFBFBD>css|]}t|<01>VqdS)N)rD)<02>.0<EFBFBD>prrr<00> <genexpr><3E><00>z.replace_text_with_set_field.<locals>.<genexpr>)<10>findallrrDrxr?<00>_make_text_runr
r^<00>list<73>index<65>remover<65><00>ranger>r@<00>sum) raZ start_run_idxZ start_offsetZ end_run_idxZ
end_offsetZ captured_textrZcompensate_spacesr Z start_runZstart_tZ original_text<78>head<61>tailZhead_runZtail_runZ
2026-07-20 13:16:17 +08:00
field_runsZcompensate_runrc<00>idxZ
2026-07-20 16:43:08 +08:00
insert_idxrFZoriginal_text_partsZridxr<78>r<>Z
first_textZ last_textZ full_textZcapturedZ to_remover<65>rrr<00>replace_text_with_set_field<6C>sn  
2026-07-20 13:16:17 +08:00

  
" 
&  
<1C>$
2026-07-20 16:43:08 +08:00
  rcCsL|dur dS|dkrdStd<02>}td<03>}|<02>td<04>d<05>||_|<01>|<02>|S)NrrPrnrr)rr6rr?r>)r?r<>r<>rrrr<00>s
r)r+)r)r+Tr<54>)rr)N)T)6<>__doc__<5F>re<72>logging<6E>copyr<00> docx.oxml.nsr<00> docx.oxmlrr]rr<00> getLogger<65>__name__rB<00>compile<6C>
IGNORECASEr9ZUSER_MARKER_RE_CLEANZW_NSZWP_NSZA_NSZPIC_NSZR_NSrr&r(r*rJrOrUrfr}r<>r<>r<>r<>r<>r<>r<>r<>r<>r<>r<>rwr<>r<>r<>r<>rr
r rrrrrr<00><module>s^    
2026-07-20 13:16:17 +08:00


($
2026-07-20 16:43:08 +08:00
J
5<00>
|>2 +9
2026-07-20 13:16:17 +08:00
.+
<00>
M