3

I'm trying to set page numbers in a word document using python-docx. I found an attribute pgNumType (pageNumberType), which I'm setting with this code:

document = Document()
section = document.add_section(WD_SECTION.CONTINUOUS)
sections = document.sections

sectPr = sections[0]._sectPr

pgNumType = OxmlElement('w:pgNumType')
pgNumType.set(qn('w:fmt'), 'decimal')
pgNumType.set(qn('w:start'), '1')

sectPr.append(pgNumType)

This code does nothing, no page numbers are in the output document. I did the same with a lnNumType attribute which is for line numbers and it worked fine. So what is it with the pgNumType attribute? The program executes without error, so the attribute exists. But anyone knows why it has no effect?

T.Poe
  • 1,949
  • 6
  • 28
  • 59
  • You've set the numbering type, but you need an actual page number field in your header or footer. – Mad Physicist Jun 09 '18 at 17:17
  • @MadPhysicist It looks like footers/headers haven't been implemented in python docx yet: https://github.com/python-openxml/python-docx/issues/104 Or did you mean something else? – T.Poe Jun 09 '18 at 17:54
  • No, that's exactly what I meant. Your best bet is to create a blank document in Word, add the page numbering field to it, and append your program's output to that instead of to the default blank document python-docx provides you with. – Mad Physicist Jun 09 '18 at 18:06
  • Thanks for suggestion, I used a prepared document. Works like charm :-) – T.Poe Jun 09 '18 at 18:41

2 Answers2

1

While your page style setting is fine, but it does not automatically insert a page number field anywhere in your document. Normally, page numbers appear in a header or a footer, but unfortunately, python-docx does not currently support headers, footers or fields. The former two appear to be an ongoing work in progress: https://github.com/python-openxml/python-docx/issues/104.

The linked issue mentions a number of workarounds. The one I have found to be most robust is to create an otherwise blank document with the headers and footers set up exactly the way you want in MS Word. You can then load and append to that document instead of the default template that docx.Document returns.

This technique is implied to be the suggested method for editing headers in the official documentation:

A lot of how a document looks is determined by the parts that are left when you delete all the content. Things like styles and page headers and footers are contained separately from the main content, allowing you to place a good deal of customization in your starting document that then appears in the document you produce.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
0

@T.Poe I was working on same problem.

new_section = document.add_section()  # Added new section for assigning different footer on each page.
sectPr = new_section._sectPr

pgNumType = OxmlElement('w:pgNumType')
pgNumType.set(qn('w:fmt'), 'decimal')
pgNumType.set(qn('w:start'), '1')

sectPr.append(pgNumType)

new_footer = new_section.footer  # Get footer-area of the recent section in document
new_footer.is_linked_to_previous = False  
footer_para = new_footer.add_paragraph()  
run_footer = footer_para.add_run("Your footer here")
_add_number_range(run_footer)
font = run_footer.font
font.name = 'Arial'
font.size = Pt(8)
footer_para.paragraph_format.page_break_before = True

This worked for me :) I don't know whats not working for you. I just created a new section

Some code for function I used to define field is as follows:

def _add_field(run, field):
    """ add a field to a run
    """
    fldChar1 = OxmlElement('w:fldChar')  # creates a new element
    fldChar1.set(qn('w:fldCharType'), 'begin')  # sets attribute on element
    instrText = OxmlElement('w:instrText')
    instrText.set(qn('xml:space'), 'preserve')  # sets attribute on element
    instrText.text = field

    fldChar2 = OxmlElement('w:fldChar')
    fldChar2.set(qn('w:fldCharType'), 'separate')
    t = OxmlElement('w:t')
    t.text = "Seq"
    fldChar2.append(t)

    fldChar4 = OxmlElement('w:fldChar')
    fldChar4.set(qn('w:fldCharType'), 'end')


    r_element = run._r
    r_element.append(fldChar1)
    r_element.append(instrText)
    r_element.append(fldChar2)
    r_element.append(fldChar4)



def _add_number_range(run):
    """ add a number range field to a run
    """
    _add_field(run, r'Page')
P.Natu
  • 131
  • 1
  • 3
  • 12