For today’s blog entry, I’m going to delve into a bit of Word esoterica that, if you’re into this sort of thing, you might find fascinating, and if you’re not, well, you have been warned<g>.
If a document is changed in Word, you’d expect to get a prompt when you close the document, wouldn’t you?
Well, that’s apparently just expecting too much, my friend.
And if you were to load a document programmatically and simply iterate through it, without changing anything, you’d expect that the document wouldn’t actually change, wouldn’t you?
Well, that’s also… oh, you get the picture.
It appears to be possible in Word, without programming, to create a header that doesn’t actually exist, or rather, that exists but is completely empty.
If you then iterate through the headers of the document, retrieving the Range of each, the act of simply “retrieving” the range, not actually modifying it, will cause the header to be modified, inserting a paragraph mark where there was none before.
This can effectively shift the entire document down, since, in the original document there was no “paragraph” in the header, but now there is.
Even more interesting, this modification isn’t logged internally by Word, so if you were to close the document, you get no prompt that a change has been made, and from what I can tell, the change isn’t actually saved, although it’s very visible on-screen.
You can test this by creating a document with a null header, then, adding this VBA code and running it:
Sub Test() Dim sct As Section Dim r As Range Dim hdr As HeaderFooter For Each sct In Me.Sections For Each hdr In sct.Headers Set r = hdr.Range Next Next End Sub
How do you create a document with a null header? To be perfectly honest, neither I nor a Microsoft Tech could figure that one out. Please let me know if you can.
But what I did discover was that the situation yielded something you could detect.
Essentially, the problem is this:
Each SECTION object in Word has a PAGESETUP object associated with it.
And each PAGESETUP object has an associated HEADERDISTANCE and TOPMARGIN property. It also has FOOTERDISTANCE and BOTTOMMARGIN properties, but I’ll only deal with headers here. Footers are handled in exactly the same way.
In a document’s section, if the HEADERDISTANCE is ever >= the TOPMARGIN, any changes in the size of the header have the potential for “spilling out” over the limit of the margin and pushing the content of the section body down.
I say potential, because if the header doesn’t contain anything, or if what it contains is not enough to push the bottom of the header past the TOPMARGIN, everything will stay put and all will be well.
In any case, right before you save the document, you simply need to iterate through all the headers in all the sections of the document and test if they’re empty. If they are, you force the HeaderDistance back to half of the TopMargin.
Section.PageSetup.HeaderDistance = Section.PageSetup.TopMargin / 2
Why half? It essentially doesn’t matter what it is, as long as it’s less than the TopMargin.
But what makes up an “empty” header?
Once you get the Range of the header
set Range = Header.Range
Make sure it’s not a linked header
If Range.LinkToPrevious then {it's linked so don't bother with it}
and then check the character and word counts
If Range.Characters.Count > 1 and Range.Words.Count > 0 and Range.Characters.Count > Range.Words.Count Then {this is NOT an empty header} End If
If it’s empty, adjust the HeaderDistance as noted above.
Fun stuff.