Replace String in XML while keeping function

Question:

I’m trying to generate a Word document with data based on a template using python-docx. For the most part, I’ve managed to work out something. However, my document has some conditions that I am trying to fulfil, namely I need to generate certain data only in the last page.

Thing is, I don’t know what the "last page" is as that depends on the amount of data added into the template, so it could be 1, 2, or 50. Python-docx, like a lot of other similar libraries like Reportlab, has no function to indicate what is the last page (at least, none that I found). So, after loads of research, I came across something like a function that can be created in Microsoft Word that I implemented in my template:

{ IF PAGE=NUMPAGES testVar }

and I have managed to make it so that testVar only displays on the last page.

Now, the issue is that I need to replace testVar with an actual calculated value through Python before the document gets generated.
I previously tried:

theFooter = section.footer.tables[0]
theFooter.cell(0,2).text = 1000

(the aforementioned function is in a table in the footer in the template)

And while it replaces the testVar with 1000, the docx function in the template was also replaced. So now I’m stuck, trying to figure out how to replace testVar without removing the docx function so that my 100 value is displayed only on the last page, wherever that may be.

A crop of the template in question

(The 100 value is just a placeholder variable, corresponding to testVar)

How can I do this exactly? I have hunted through Stack Overflow, and came across some similar issues but they’re ultimately different from what I need.

Asked By: Erik

||

Answers:

After much reading, think I may have finally found the solution, albeit indirectly.

According to this and this (and I made a verification through dir() ), each _element has a ‘text’ attribute as well.

So, I did something along these lines:

    theFooter = document.sections[0].footer.tables[0]
    for run in theFooter.cell(1,3).paragraphs[0].runs:
        if 'w:instrText' in run._element.xml and 'xml:space="preserve"' in run._element.xml:
            if (run._element.xml.find("testVar") >= 0): run._element.text= "1000"

And this seems to successfully replace ‘testVar’ in my template with the ‘1000’ string without affecting the docx function when I generate the word document, resulting in ‘1000’ being printed only on the footer of the very last page.

The only strange part is that when I run a printout using:

theFooter.cell(1,3).text

The output I got was "1000testVar", so I’m not sure whether this will matter in the long run.

Edit June 2, 2020

Welp, found a limitation on this(or it could be related to the Word function, I’m not sure): the replacement text cannot have any spacing within (eg. "Replace Var" compared to "ReplaceVar"), else all it does is replace "testVar" with "Var" (as opposed to replacing "testVar" with "Replace Var") and "Replace" will STILL be shown in the output.

Edit Sept 9, 2020

Just thought I should update this that I found a fix to the aforementioned limitation.

All I needed to do was the encapsulate the variable in the template within a quotation "" marks, like so:

{IF PAGE=NUMPAGES "testVar" }

and it works as expected; properly replacing the testVar variable with the data, even if said data was a text with spacing.(eg. "100 times")

Answered By: Erik