首页 文章

如何使用python-docx将复选框表单插入到.docx文件中?

提问于
浏览
1

我一直在使用python来实现自定义解析器,并使用解析后的数据来格式化要在内部分发的word文档 . 到目前为止,所有格式都很简单易行,但我完全不知道如何将复选框插入到单个表格单元格中 .

我尝试在python-docx中使用python对象函数(使用 get_or_add_tcPr() 等),这会导致MS Word在我尝试打开文件时抛出以下错误"The file xxxx cannot be opened because there are problems with the contents Details: The file is corrupt and cannot be opened" .

经过一段时间的努力,我转向了第二种方法,涉及操作输出文档的word / document.xml文件 . 我已经检索了我认为是保存为 replacementXML 的复选框的正确xml,并将填充文本插入到单元格中以充当可以搜索和替换的标记 searchXML . 以下似乎在linux(Fedora 25)环境中使用python运行,但是当我尝试打开文档时word文档显示相同的错误,但是这次文档可以恢复并恢复为填充文本 . 我've been able to get this to work with a manually made document and using an empty table cell, so I believe that this should be possible. NOTE: I' ve包含了 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

我有一种强烈的感觉,通过python-docx库有一个更简单(和正确!)的方式,但由于某种原因,我似乎无法做到正确 .

有没有办法轻松地将复选框字段插入MS Word文档中的表格单元格?如果是的话,我该怎么做?如果不是,有没有比操作.xml文件更好的方法?

更新:我已经能够使用python-docx成功地将XML注入到文档中,但是复选框和添加的XML没有出现 .

我已将以下XML添加到表格单元格中:

<w:tc>
  <w:tcPr>
    <w:tcW w:type="dxa" w:w="4320"/>
    <w:gridSpan w:val="2"/>
  </w:tcPr>
  <w:p>
    <w:r>
      <w:bookmarkStart w:id="0" w:name="testName">
        <w:complexType w:name="CT_FFCheckBox">
          <w:sequence>
            <w:choice>
              <w:element w:name="size" w:type="CT_HpsMeasure"/>
              <w:element w:name="sizeAuto" w:type="CT_OnOff"/>
            </w:choice>
            <w:element w:name="default" w:type="CT_OnOff" w:minOccurs="0"/>
            <w:element w:name="checked" w:type="CT_OnOff" w:minOccurs="0"/>
          </w:sequence>
        </w:complexType>
      </w:bookmarkStart>
      <w:bookmarkEnd w:id="0" w:name="testName"/>
    </w:r>
  </w:p>
</w:tc>

通过使用以下python-docx代码:

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)

似乎无法找到XML没有反映在输出文档中的推理,但会更新我发现的任何内容 .

2 回答

  • 1

    经过@scanny的大量挖掘和帮助,我终于能够做到这一点 .

    可以使用以下函数将复选框插入到python-docx中的任何段落中 . 我正在将一个复选框插入表格中的特定单元格 .

    def addCheckbox(para, box_id, name):
    
    run = para.add_run()
    tag = run._r
    fld = docx.oxml.shared.OxmlElement('w:fldChar')
    fld.set(docx.oxml.ns.qn('w:fldCharType'), 'begin')
    fldData = docx.oxml.shared.OxmlElement('w:fldData')
    
    fldData.text = '/////2UAAAAUAAYAQwBoAGUAYwBrADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
    fldData.set(docx.oxml.ns.qn('xml:space'), 'preserve')
    fld.append(fldData)
    tag.append(fld)
    
    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表单中获取带有现有复选框的word文档 . 如果不设置此文本,该功能将失败 . 我还没有确认,但我听说过一个开发人员随意更改字符串的情况,但一旦保存,它将恢复为原始生成的值 .

    希望这有助于某人!

  • 3

    这些变通办法功能的关键在于拥有一个可行的XML示例,并能够比较您生成的XML . 如果生成与工作示例匹配的XML,则每次都会有效 . opc-diag 对于检查Word文档中的XML非常方便 . 使用非常小的文档(如单段或双行表,用于分析目的)可以更容易地弄清楚Word如何构建XML .

    需要注意的一点是,Word文档中的XML元素是序列敏感的,这意味着任何其他元素中的子元素通常都具有必须出现的设置顺序 . 如果你换了这个,你会得到你提到的"repair"错误 .

    我发现从 python-docx 中操作XML要容易得多,因为它会处理所有解压缩和重新拉链,以及许多其他细节 .

    为了使排序正确,你'll need to be familiar with the XML Schema specifications for the elements you'正在使用 . 这里有一个例子:http://python-docx.readthedocs.io/en/latest/dev/analysis/features/text/paragraph-format.html

    完整模式位于 ref/xsd/ 下的代码树中 . 文本的大多数元素都在 wml.xsd 文件中(wml代表WordProcessing标记语言) .

    您可以通过搜索 "python-docx" workaround function 找到其他所谓的"workaround functions"的示例 . 请特别注意 parse_xml() 函数和 OxmlElement 对象,这些对象将允许您分别创建新的XML子树和单个元素 . 可以使用常规 lxml._Element 方法定位XML元素; python-docx 中的所有XML元素都基于 lxml . http://lxml.de/api/lxml.etree._Element-class.html

相关问题