Search code examples
python-3.xdocx

python-docx : adding checkbox to .docx doesn't work



I want to add xml to my .docx document using python-docx library. I tried this code from stackoverflow but it doesn't work, I don't know why. I get nothing when opening the docx with LibreOffice and Microsoft word.

table = document.add_table(rows=1, cols=1)
p = table.cell(0, 0).paragraphs[0]
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'), '0')
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'), '0')
tag.append(end)

Can you help me? Thank you very much
PS : I find the code here


Solution

  • Tweaking the function from this SO post, I got this to work.

    import docx
    import random
    
    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')
    
        ffData = docx.oxml.shared.OxmlElement('w:ffData')
        e = docx.oxml.shared.OxmlElement('w:name')
        e.set(docx.oxml.ns.qn('w:val'), 'Check1')
        ffData.append(e)
        ffData.append(docx.oxml.shared.OxmlElement('w:enabled'))
        e = docx.oxml.shared.OxmlElement('w:calcOnExit')
        e.set(docx.oxml.ns.qn('w:val'), '0')
        ffData.append(e)
        e = docx.oxml.shared.OxmlElement('w:checkBox')
        e.append(docx.oxml.shared.OxmlElement('w:sizeAuto'))
        ee = docx.oxml.shared.OxmlElement('w:default')
        ee.set(docx.oxml.ns.qn('w:val'), '0')
        e.append(ee)
        ffData.append(e)
    
        fld.append(ffData)
        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)
    
    if __name__ == '__main__':
        document = docx.Document()
        document.add_heading('Document Title', 0)
        p1 = document.add_paragraph('A paragraph with a checkbox ')
        addCheckbox(p1, random.randint(16*1024, 32*1024), 'justinwashere')
        document.save('demo.docx')
    

    I changed the contents of his ffData in the function to match the replacement XML in his original question.