如何使用python docx将复选框表单插入到.docx文件中?
我一直在使用python实现一个自定义解析器,并使用解析后的数据格式化要在内部分发的word文档。到目前为止,所有的格式都是简单明了的,但如何在单个表单元格中插入复选框,我完全被难住了 我曾尝试在python docx中使用python对象函数(使用如何使用python docx将复选框表单插入到.docx文件中?,python,xml,checkbox,python-docx,Python,Xml,Checkbox,Python Docx,我一直在使用python实现一个自定义解析器,并使用解析后的数据格式化要在内部分发的word文档。到目前为止,所有的格式都是简单明了的,但如何在单个表单元格中插入复选框,我完全被难住了 我曾尝试在python docx中使用python对象函数(使用get\u或\u add\u tcPr(),等等),这会导致MS Word在尝试打开文件时抛出以下错误,“文件xxxx无法打开,因为内容详细信息有问题:文件已损坏且无法打开” 经过一段时间的努力,我转向了第二种方法,涉及到为输出文档操作word/do
get\u或\u add\u tcPr()
,等等),这会导致MS Word在尝试打开文件时抛出以下错误,“文件xxxx无法打开,因为内容详细信息有问题:文件已损坏且无法打开”
经过一段时间的努力,我转向了第二种方法,涉及到为输出文档操作word/document.xml文件。我已经为保存为replacementXML
的复选框检索了我认为正确的xml,并将填充文本插入单元格中,作为可以搜索和替换的标记,searchXML
。以下内容似乎是在linux(Fedora 25)环境中使用python运行的,但是当我尝试打开文档时,word文档显示相同的错误,但是这次文档是可恢复的,并恢复为填充文本。我已经能够使用一个手工制作的文档和一个空的表格单元格来实现这一点,所以我相信这应该是可能的。注意:我已经在searchXML
变量中包含了表格单元格的整个xml元素,但是我尝试了使用正则表达式并缩短字符串。不只是使用一个精确的匹配,因为我知道这可能会因细胞而异
searchXML = r'<w:tc><w:tcPr><w:tcW w:type="dxa" w:w="4320"/><w:gridSpan w:val="2"/></w:tcPr><w:p><w:pPr><w:jc w:val="right"/></w:pPr><w:r><w:rPr><w:sz w:val="16"/></w:rPr><w:t>IN_CHECKB</w:t></w:r></w:p></w:tc>'
def addCheckboxes():
os.system("mkdir unzipped")
os.system("unzip tempdoc.docx -d unzipped/")
with open('unzipped/word/document.xml', encoding="ISO-8859-1") as file:
filedata = file.read()
rep_count = 0
while re.search(searchXML, filedata):
filedata = replaceXML(filedata, rep_count)
rep_count += 1
with open('unzipped/word/document.xml', 'w') as file:
file.write(filedata)
os.system("zip -r ../buildcfg/tempdoc.docx unzipped/*")
os.system("rm -rf unzipped")
def replaceXML(filedata, rep_count):
replacementXML = r'<w:tc><w:tcPr><w:tcW w:w="4320" w:type="dxa"/><w:gridSpan w:val="2"/></w:tcPr><w:p w:rsidR="00D2569D" w:rsidRDefault="00FD6FDF"><w:pPr><w:jc w:val="right"/></w:pPr><w:r><w:rPr><w:sz w:val="16"/>
</w:rPr><w:fldChar w:fldCharType="begin"><w:ffData><w:name w:val="Check1"/><w:enabled/><w:calcOnExit w:val="0"/><w:checkBox><w:sizeAuto/><w:default w:val="0"/></w:checkBox></w:ffData></w:fldChar>
</w:r><w:bookmarkStart w:id="' + rep_count + '" w:name="Check' + rep_count + '"/><w:r><w:rPr><w:sz w:val="16"/></w:rPr><w:instrText xml:space="preserve"> FORMCHECKBOX </w:instrText></w:r><w:r>
<w:rPr><w:sz w:val="16"/></w:rPr></w:r><w:r><w:rPr><w:sz w:val="16"/></w:rPr><w:fldChar w:fldCharType="end"/></w:r><w:bookmarkEnd w:id="' + rep_count + '"/></w:p></w:tc>'
filedata = re.sub(searchXML, replacementXML, filedata, 1)
rerturn filedata
似乎找不到理由说明XML没有反映在输出文档中,但会用我找到的任何内容进行更新。这些变通功能的关键是要有一个有效的XML示例,并能够比较生成的XML。如果生成的XML与工作示例相匹配,则每次都可以工作
opcdiag
便于检查Word文档中的XML。使用非常小的文档(如用于分析目的的单段落或两行表)可以更容易地确定Word是如何构造XML的
需要注意的一点是,Word文档中的XML元素是序列敏感的,这意味着任何其他元素中的子元素通常都有一个它们必须出现的设置顺序。如果你换了这个,你就会得到你提到的“修复”错误
我发现从pythondocx
中操作XML要容易得多,因为它为您处理所有的解压缩和重新压缩,以及许多其他细节
为了正确排序,您需要熟悉所使用元素的XML模式规范。这里有一个例子:
完整架构位于代码树中的ref/xsd/
下。文本的大多数元素都在wml.xsd
文件中(wml代表字处理标记语言)
您可以通过搜索
“python docx”工作区函数
,找到其他所谓的“工作区函数”的示例。请特别注意parse_xml()
函数和OxmlElement
对象,它们将允许您分别创建新的xml子树和单个元素。可以使用常规的lxml.\u Element
方法定位XML元素;pythondocx
中的所有XML元素都基于lxml
经过@scanny的大量挖掘和帮助,我终于能够完成这项任务
可以使用以下函数将复选框插入到python docx
中的任何段落中。我正在将复选框插入表中的特定单元格中
def addCheckbox(para, box_id, name, checked):
run = para.add_run()
tag = run._r
fldchar = docx.oxml.shared.OxmlElement('w:fldChar')
fldchar.set(docx.oxml.ns.qn('w:fldCharType'), 'begin')
ffdata = docx.oxml.shared.OxmlElement('w:ffData')
name = docx.oxml.shared.OxmlElement('w:name')
name.set(docx.oxml.ns.qn('w:val'), cb_name)
enabled = docx.oxml.shared.OxmlElement('w:enabled')
calconexit = docx.oxml.shared.OxmlElement('w:calcOnExit')
calconexit.set(docx.oxml.ns.qn('w:val'), '0')
checkbox = docx.oxml.shared.OxmlElement('w:checkBox')
sizeauto = docx.oxml.shared.OxmlElement('w:sizeAuto')
default = docx.oxml.shared.OxmlElement('w:default')
if checked:
default.set(docx.oxml.ns.qn('w:val'), '1')
else:
default.set(docx.oxml.ns.qn('w:val'), '0')
checkbox.append(sizeauto)
checkbox.append(default)
ffdata.append(name)
ffdata.append(enabled)
ffdata.append(calconexit)
ffdata.append(checkbox)
fldchar.append(ffdata)
tag.append(fldchar)
run2 = para.add_run()
tag2 = run2._r
start = docx.oxml.shared.OxmlElement('w:bookmarkStart')
start.set(docx.oxml.ns.qn('w:id'), str(box_id))
start.set(docx.oxml.ns.qn('w:name'), name)
tag2.append(start)
run3 = para.add_run()
tag3 = run3._r
instr = docx.oxml.OxmlElement('w:instrText')
instr.text = 'FORMCHECKBOX'
tag3.append(instr)
run4 = para.add_run()
tag4 = run4._r
fld2 = docx.oxml.shared.OxmlElement('w:fldChar')
fld2.set(docx.oxml.ns.qn('w:fldCharType'), 'end')
tag4.append(fld2)
run5 = para.add_run()
tag5 = run5._r
end = docx.oxml.shared.OxmlElement('w:bookmarkEnd')
end.set(docx.oxml.ns.qn('w:id'), str(box_id))
end.set(docx.oxml.ns.qn('w:name'), name)
tag5.append(end)
return
fldData.text
对象看起来是随机的,但它是从生成的XML表单中获取的,并带有一个现有的复选框。如果未设置此文本,该函数将失败。我还没有确认,但我听说过一个场景,开发人员任意更改字符串,但一旦保存,它将恢复为原始生成的值。感谢您的回复scanny!我现在正在研究模式,并尝试将其应用于更简单的(python生成的)word文档。我将更新我的进度和出现的任何其他问题。同时,感谢您在社区中如此活跃。如果不是你的回答,我不会在这个问题上取得任何进展!您好@scanny,我一直在尝试让parse_xml()函数为复选框工作,但是我收到一个lxml.etree.XMLSyntaxError,抱怨没有定义名称。我理解这个错误,但对XML不是很熟悉,也不知道如何正确添加define。我使用的是您提供的模式中的XML。我使用了一些简单的文档来理解复选标记的顺序,它似乎需要单元格中的段落。使用“cell.\u tc.\u add\u p”然后插入复选框的xml是否正确?非常感谢您的帮助!完美的行!我已经用我的进度更新了这个问题。知道为什么文档没有反映我添加的XML吗?我完全理解!我昨天不能参加比赛,因为我的“分数”低于最低水平。我非常感谢你的帮助!您好,它的工作方式是否与我尝试打印“FORMCHECKBOX”文本时的工作方式相同,而不是打印复选框
run = p.add_run()
tag = run._r
start = docx.oxml.shared.OxmlElement('w:bookmarkStart')
start.set(docx.oxml.ns.qn('w:id'), '0')
start.set(docx.oxml.ns.qn('w:name'), n)
tag.append(start)
ctype = docx.oxml.OxmlElement('w:complexType')
ctype.set(docx.oxml.ns.qn('w:name'), 'CT_FFCheckBox')
seq = docx.oxml.OxmlElement('w:sequence')
choice = docx.oxml.OxmlElement('w:choice')
el = docx.oxml.OxmlElement('w:element')
el.set(docx.oxml.ns.qn('w:name'), 'size')
el.set(docx.oxml.ns.qn('w:type'), 'CT_HpsMeasure')
el2 = docx.oxml.OxmlElement('w:element')
el2.set(docx.oxml.ns.qn('w:name'), 'sizeAuto')
el2.set(docx.oxml.ns.qn('w:type'), 'CT_OnOff')
choice.append(el)
choice.append(el2)
el3 = docx.oxml.OxmlElement('w:element')
el3.set(docx.oxml.ns.qn('w:name'), 'default')
el3.set(docx.oxml.ns.qn('w:type'), 'CT_OnOff')
el3.set(docx.oxml.ns.qn('w:minOccurs'), '0')
el4 = docx.oxml.OxmlElement('w:element')
el4.set(docx.oxml.ns.qn('w:name'), 'checked')
el4.set(docx.oxml.ns.qn('w:type'), 'CT_OnOff')
el4.set(docx.oxml.ns.qn('w:minOccurs'), '0')
seq.append(choice)
seq.append(el3)
seq.append(el4)
ctype.append(seq)
start.append(ctype)
end = docx.oxml.shared.OxmlElement('w:bookmarkEnd')
end.set(docx.oxml.ns.qn('w:id'), '0')
end.set(docx.oxml.ns.qn('w:name'), n)
tag.append(end)
def addCheckbox(para, box_id, name, checked):
run = para.add_run()
tag = run._r
fldchar = docx.oxml.shared.OxmlElement('w:fldChar')
fldchar.set(docx.oxml.ns.qn('w:fldCharType'), 'begin')
ffdata = docx.oxml.shared.OxmlElement('w:ffData')
name = docx.oxml.shared.OxmlElement('w:name')
name.set(docx.oxml.ns.qn('w:val'), cb_name)
enabled = docx.oxml.shared.OxmlElement('w:enabled')
calconexit = docx.oxml.shared.OxmlElement('w:calcOnExit')
calconexit.set(docx.oxml.ns.qn('w:val'), '0')
checkbox = docx.oxml.shared.OxmlElement('w:checkBox')
sizeauto = docx.oxml.shared.OxmlElement('w:sizeAuto')
default = docx.oxml.shared.OxmlElement('w:default')
if checked:
default.set(docx.oxml.ns.qn('w:val'), '1')
else:
default.set(docx.oxml.ns.qn('w:val'), '0')
checkbox.append(sizeauto)
checkbox.append(default)
ffdata.append(name)
ffdata.append(enabled)
ffdata.append(calconexit)
ffdata.append(checkbox)
fldchar.append(ffdata)
tag.append(fldchar)
run2 = para.add_run()
tag2 = run2._r
start = docx.oxml.shared.OxmlElement('w:bookmarkStart')
start.set(docx.oxml.ns.qn('w:id'), str(box_id))
start.set(docx.oxml.ns.qn('w:name'), name)
tag2.append(start)
run3 = para.add_run()
tag3 = run3._r
instr = docx.oxml.OxmlElement('w:instrText')
instr.text = 'FORMCHECKBOX'
tag3.append(instr)
run4 = para.add_run()
tag4 = run4._r
fld2 = docx.oxml.shared.OxmlElement('w:fldChar')
fld2.set(docx.oxml.ns.qn('w:fldCharType'), 'end')
tag4.append(fld2)
run5 = para.add_run()
tag5 = run5._r
end = docx.oxml.shared.OxmlElement('w:bookmarkEnd')
end.set(docx.oxml.ns.qn('w:id'), str(box_id))
end.set(docx.oxml.ns.qn('w:name'), name)
tag5.append(end)
return