PDA

View Full Version : [SOLVED:] Footnote page numbers



MacroShadow
10-24-2012, 01:48 PM
Is there a way, using vba, to determine if a footnote ends on the same page it begun on?

If the footnote flows over to the next page Selection.Range.Characters.First.Information(3) returns the number of the beginning page, not the current page.

Frosty
10-24-2012, 03:33 PM
There may be a more efficient way to do this, but this seems to work...


Public Function fFootNoteIsSplit(Optional iFootnote As Integer = 1) As Boolean
Dim f As Footnote
Dim char As Range
Dim iNumFirstLines As Integer
If ActiveDocument.Footnotes.Count = 0 Then
fFootNoteIsSplit = False
End If
Set f = ActiveDocument.Footnotes(iFootnote)
For Each char In f.Range.Words
If char.Information(wdVerticalPositionRelativeToTextBoundary) = 0 Then
If iNumFirstLines > 0 Then
fFootNoteIsSplit = True
Exit For
End If
Else
iNumFirstLines = iNumFirstLines + 1
End If
Next
End Function

Frosty
10-24-2012, 03:35 PM
Actually, I'm sure there's a more efficient way than cycling through all of the characters in the footnote's range... just using .Words instead of .Characters (which I've already changed) would give less cycles to the loop, but the essential approach has to be something related to checking the vertical position (at least, I *think* it does).

But without using selection, you can't really move line by line, so .Words seems like it might be as good as it gets, because the footnote might be a single paragraph...

I'm curious if anyone comes up with something else... you always seem to have interesting little problems, MacroShadow :)

MacroShadow
10-24-2012, 04:11 PM
Thank you.


you always seem to have interesting little problems, MacroShadow :)
This question I asked on behalf of a co-worker. lol.

Paul_Hossler
10-24-2012, 05:43 PM
Actually how did you get a footnote to 'flow' to the next page?

As I add text to the footnote, the footnote line keeps climbing up the page, and the vertical size of the footnote gets bigger. (Word 2010)

A second footnote keeps climbing

I could probalby have a 1/2 page footnote section at that rate.

It would be nice if any footnote or footnotes that required more than a set space would continue on the next page in the footnote position

Paul

Frosty
10-24-2012, 05:45 PM
A footnote will fill up the entire page, as long as it starts on the same page as its reference. Try inserting your footnote closer to the bottom of the page to begin with.

Paul_Hossler
10-24-2012, 06:47 PM
thanks

I thought that at one time there was a setting as to how much footnote space there was at the bottom of the page, after margins and footers.

Guess not

Seems funny that it'd be sensitve to where the foot note is inserted

Paul

gmaxey
10-24-2012, 07:26 PM
For a simple example, pages and rectangles seems to work:


Sub ScratchMacro()
Dim oPage As Page
Dim oRect As Rectangle
Dim oRng As Word.Range
Dim oRngFN As Word.Range
Set oRngFN = ActiveDocument.Footnotes(1).Range
Set oPage = ActiveDocument.ActiveWindow.Panes(1).Pages(ActiveDocument.Footnotes(1).Rang e.Information(3))
Set oRect = oPage.Rectangles(3) '1 is main text, 2 is the separator 3 is the footnotes.
'not sure this would always be the case
Set oRng = oRect.Range
If oRngFN.InRange(oRng) Then
Debug.Print "FN is on same page"
Else
Debug.Print "FN spans multiple pages."
End If
End Sub

MacroShadow
10-25-2012, 07:23 AM
Frosty,

There is one problem with your function. If the footnote spans over multiple columns on the same page it still returns True.

MacroShadow
10-25-2012, 07:32 AM
Greg,

Is there a way to turn your macro into a function that will take the footnote number as a parameter (like Frosty's code)?

I tried but couldn't get it to work.

gmaxey
10-25-2012, 09:32 AM
Sub Test()
MsgBox ScratchMacro(1)
End Sub

Function ScratchMacro(lngIndex) As String
Dim oPage As Page
Dim oRect As Rectangle
Dim oRng As Word.Range
Dim oRngFN As Word.Range
Set oRngFN = ActiveDocument.Footnotes(lngIndex).Range
Set oPage = ActiveDocument.ActiveWindow.Panes(1).Pages(ActiveDocument.Footnotes(lngInde x).Range.Information(3))
Set oRect = oPage.Rectangles(3) '1 is main text, 2 is the separator 3 is the footnotes.
'not sure this would always be the case
Set oRng = oRect.Range
If oRngFN.InRange(oRng) Then
ScratchMacro = "Yes, the FN is on same page"
Else
ScratchMacro = "No, the FN spans multiple pages."
End If
End Function

MacroShadow
10-25-2012, 09:46 AM
That is basically what I tried.

If the footnote is not in the first column (in a multi column layout) it returns: No, the FN spans multiple pages even when the footnote doesn't flow over to the next page.

Frosty
10-25-2012, 09:54 AM
can you post a sample doc, MacroShadow? I don't even know how your set up is... this is back to your custom footnotes with linked textboxes, right?

I *suspect* you can use my function (at least) and simply check for a reset twice... but it would need to be modified somewhat to assume that the footnotes are always in two columns. Otherwise you need a separate function to test if this is a two-column footnote set up or a one-column footnote setup...

gmaxey
10-25-2012, 10:02 AM
That is not a condition in your original post. It will help if you fully define the scope of what you are after.

This will probably work for 2 columns, but not for 1 or 3:


Sub Test()
MsgBox ScratchMacro(1)
End Sub

Function ScratchMacro(lngIndex) As String
Dim oPage As Page
Dim oRect As Rectangle
Dim oRect2 As Rectangle
Dim oRng As Word.Range
Dim oRng2 As Word.Range
Dim oRngFN As Word.Range
Set oRngFN = ActiveDocument.Footnotes(lngIndex).Range
Set oPage = ActiveDocument.ActiveWindow.Panes(1).Pages(ActiveDocument.Footnotes(lngInde x).Range.Information(3))
Set oRect = oPage.Rectangles(4) '1 is main text column 1, 2 is main text column 2, 3 is the col 1separator
'4 is col1 footnotes, 5 is col2 separator, 6 is col 2 footnotes.
Set oRect2 = oPage.Rectangles(6)
'not sure this would always be the case
Set oRng = oRngFN.Duplicate
Set oRng2 = oRngFN.Duplicate
oRng.Collapse wdCollapseStart
oRng2.Collapse wdCollapseEnd
If oRng.InRange(oRect.Range) And oRng2.InRange(oRect2.Range) Then
ScratchMacro = "Yes, the FN is on same page"
Else
ScratchMacro = "No, the FN spans multiple pages."
End If
End Function

gmaxey
10-25-2012, 10:07 AM
It will also help if you post what you've tried. This way people can spend their time trying to help you with what you've tried rather than spend it redoing what you, after the fact, inform us that you have already done.



That is basically what I tried.

If the footnote is not in the first column (in a multi column layout) it returns: No, the FN spans multiple pages even when the footnote doesn't flow over to the next page.

Frosty
10-25-2012, 11:46 AM
I agree with Greg: MacroShadow, you've been posting long enough here that you can give better information sooner, and post the kinds of things you've tried.

You're getting to the point that I got when I started programming. I kept asking my mentor "what about this, what about that" and he continued to patiently teach me. But then, at some point, his responses started becoming "Umm, I don't know-- figure it out. That's the job."

You won't get that response here, but there is a point where you're good enough to put in effort too.

That said... this is considerably more tricky, and this is what I've come up with. It seems to work regardless of the number of columns, or what column the initial footnote reference starts in.


Public Function fFootNoteIsSplit(Optional iFootnote As Integer = 1, Optional oDoc As Document) As Boolean
Dim fn As Footnote
Dim rngWord As Range
Dim iNumFirstLinesInFootnote As Integer
Dim iNumColumnsInSection As Integer
Dim lLocation As Long
On Error GoTo l_err
'if no document passed, use the active document
If oDoc Is Nothing Then
Set oDoc = ActiveDocument
End If
'if no footnotes, then return false and exit
If oDoc.Footnotes.Count = 0 Then
fFootNoteIsSplit = False
MsgBox "No footnotes in this document.", vbInformation, "Footnote Checker"
GoTo l_exit
End If
'get the footnote
Set fn = oDoc.Footnotes(iFootnote)
'see how many columns in the footnote reference's section
iNumColumnsInSection = fn.Reference.Sections.First.PageSetup.TextColumns.Count
For Each rngWord In fn.Range.Words
'check to see if this word is the first word, or the beginning of a text column
If rngWord = fn.Range.Words.First _
Or rngWord.Information(wdVerticalPositionRelativeToTextBoundary) = 0 _
And rngWord.Information(wdHorizontalPositionRelativeToTextBoundary) = 0 Then
'check where it's horizontally located on the page, relative to any previous setting
If rngWord.Information(wdHorizontalPositionRelativeToPage) > lLocation Then
lLocation = rngWord.Information(wdHorizontalPositionRelativeToPage)
'also, increment our first line counter
iNumFirstLinesInFootnote = iNumFirstLinesInFootnote + 1
'if we've got a location that is closer to the left than a previous setting,
'we know we've hit a new page, so the range is split... we're done
Else
fFootNoteIsSplit = True
Exit For
End If
End If
'if we've gotten more first lines than columns, we know it's split
If iNumFirstLinesInFootnote > iNumColumnsInSection Then
fFootNoteIsSplit = True
Exit For
End If
Next
l_exit:
Exit Function
l_err:
MsgBox "Error in this function-- decide what you want to return"
Resume l_exit
End Function

Frosty
10-25-2012, 01:10 PM
Alright-- so I played around with the .Pages and .Rectangles collections a bit. Not to take the original post too off-topic, but I don't think making the assumption on the specific rectangle number can be a good approach to this problem, because you have to make too many assumptions about the layout of the document.

The .Page object is relatively simple to access (although you *have* to be in print layout view, which is a limiter)... but the number of rectangles on a page is too much of a variable.

On a single page, two columns of main story text with a single footnote that also spans two columns... you will have 6 rectangles (as Greg's comments articulate).

However, if the right column of the page is entirely taken up with the footnote (i.e., no main story text), you'll only have 5 rectangles.

Also, if you insert a text box on the page, you'll have a totally different rectangles count. And depending on the .WrapFormat of that text box (inline, top/bottom, tight, etc), the rectangles count will adjust as well. Because the .Rectangles collection seems to be an attempt to return valid "text" areas of that particular page (somewhat analogous to the way a powerpoint slide is laid out).

Granted, I don't know alot about this area of the object model, and Greg knows a lot more-- but it seems that the only way to use the rectangles collection is to iterate through all of them on the page, and then determine whether they are valid areas you're interested in... that's what I've done using the Rectangles approach that Greg started. This seems to work as well, although it has the limitation of requiring Print View/Page Layout to be turned on -- on the other hand, it's much much more readable code and structure.


Public Function fFootNoteIsSplit_Alt1(Optional iFootnote As Integer = 1, _
Optional oDoc As Document) As Boolean
Dim r As Rectangle
Dim p As Page
Dim fn As Footnote
Dim rLastFootnoteOnPage As Range
If oDoc Is Nothing Then
Set oDoc = ActiveDocument
End If
If oDoc.ActiveWindow.ActivePane.View.Type <> wdPrintView Then
MsgBox "You must be in page layout view to use this function"
Exit Function
End If
Set fn = oDoc.Footnotes(iFootnote)
Set p = oDoc.ActiveWindow.ActivePane.Pages(1)
'cycle through the main footnote story rectangles
For Each r In p.Rectangles
If r.RectangleType = wdTextRectangle Then
If r.Range.StoryType = wdFootnotesStory Then
'get the range of the last footnote on the page
Set rLastFootnoteOnPage = r.Range.Duplicate
End If
End If
Next
'the range of whole footnote doesn't include the paragraph mark, so we add 1 to the .End test
If rLastFootnoteOnPage.End = fn.Range.End + 1 Then
fFootNoteIsSplit_Alt1 = False
Else
fFootNoteIsSplit_Alt1 = True
End If
End Function

gmaxey
10-25-2012, 01:20 PM
Jason,

You very well may have crossed the Rubicon. You may now know alot more about panes, pages, rectangles, and lines than I do.

I know about them, and I know that sometimes there are some neat tricks that you can pull off using them.

Other than that, I pass the hat. You are now the resident expert ;-)

Frosty
10-25-2012, 01:26 PM
Ha. We'll see. I have no doubt you'll surprise me with something else. I just worry areas of the object model I don't understand like a dog worries a bone. So I played a bit. It's a pretty powerful area, in terms of identifying areas on a particular page. But the limitation of page layout view (and the general concept of pages in word) probably means it has to be use in pretty narrow circumstances.

MacroShadow-- because of all the layout work you're doing, you should really explore the rectangles collection... It might simplify some of your coding.

gmaxey
10-25-2012, 02:08 PM
You know of the countless thousands of hours I've sat working with a Word document on screen, I'll bet that not five minutes of it was spent outside of print layout view.

For some people Normal (or Draft) is normal, but anything other that print layout just freaks me out.

MacroShadow
10-25-2012, 11:11 PM
Sorry for the delay, for some reason I wasn't notified by email that there were new posts. Thank you all for your help, it is muchly appreciated.


can you post a sample doc, MacroShadow? I don't even know how your set up is... this is back to your custom footnotes with linked textboxes, right?
No, this has nothing to do with the linked text boxes, now I'm dealing with Word's standard footnotes. Sample doc attached.


That is not a condition in your original post. It will help if you fully define the scope of what you are after.
Actually, in my original post I asked for a way to determine if the text flows over to the next page.


This will probably work for 2 columns, but not for 1 or 3:



Sub Test()
MsgBox ScratchMacro(1)
End Sub

Function ScratchMacro(lngIndex) As String
Dim oPage As Page
Dim oRect As Rectangle
Dim oRect2 As Rectangle
Dim oRng As Word.Range
Dim oRng2 As Word.Range
Dim oRngFN As Word.Range
Set oRngFN = ActiveDocument.Footnotes(lngIndex).Range
Set oPage = ActiveDocument.ActiveWindow.Panes(1).Pages(ActiveDocument.Footnotes(lngInde x).Range.Information(3))
Set oRect = oPage.Rectangles(4) '1 is main text column 1, 2 is main text column 2, 3 is the col 1separator
'4 is col1 footnotes, 5 is col2 separator, 6 is col 2 footnotes.
Set oRect2 = oPage.Rectangles(6)
'not sure this would always be the case
Set oRng = oRngFN.Duplicate
Set oRng2 = oRngFN.Duplicate
oRng.Collapse wdCollapseStart
oRng2.Collapse wdCollapseEnd
If oRng.InRange(oRect.Range) And oRng2.InRange(oRect2.Range) Then
ScratchMacro = "Yes, the FN is on same page"
Else
ScratchMacro = "No, the FN spans multiple pages."
End If
End Function



It returns "No, the FN spans multiple pages." for all footnotes, besides a couple when I got a runtime error 5941 :The requested member of the collection doesn't exist.


It will also help if you post what you've tried. This way people can spend their time trying to help you with what you've tried rather than spend it redoing what you, after the fact, inform us that you have already done.
You're right, I'm sorry.

Attached you will all find a sample doc.

Frosty
10-26-2012, 02:27 PM
So it seems like the rectangles approach needs more info since the .Pages(1) isn't enough to identify the actual page the footnote is on. And the other methodology seems slow to me, although it does work (footnote 16 in your attachment is split across two pages, and it is returned correctly).

So... what problem are you trying to solve, at this point? Your response was to earlier posts, but your attachment incorporates the adjustments made in later posts.

Is this solved for you, or no?

MacroShadow
10-27-2012, 10:54 AM
Frosty and Greg and anyone else who can lend a helping hand,

Frosty, Your latest function does work although it is quite slow.

I'm hoping Greg will come up with a faster working alternative.

In the meantime I cooked up a procedure that seems to work, the trouble is if I turn it into a function it no longer provides the correct result.

This works:


Sub Test()
Dim intPage, ingRec As Integer
Dim strWholeFootnote, strPartFootnote, strRecTxt As String
Dim blnFlows As Boolean
intPage = Selection.Information(3)
ingRec = ActiveDocument.ActiveWindow.Panes(1).Pages(intPage).Rectangles.Count
strRecTxt = ActiveDocument.ActiveWindow.Panes(1).Pages(intPage).Rectangles(ingRec).Rang e.Text
strWholeFootnote = Len(Selection.Footnotes(1).Range.Text)
strPartFootnote = Len(Mid(strRecTxt, InStrRev(strRecTxt, Chr(2)) + 1))
If strWholeFootnote > strPartFootnote And InStr(strRecTxt, Chr(2)) = 0 Then
blnFlows = False
ElseIf strWholeFootnote <= strPartFootnote Then
blnFlows = False
Else
blnFlows = True
End If
MsgBox blnFlows
End Sub


This doesn’t:


Function fIsSplit(Optional iFootnote As Integer = 1) As Boolean
Dim intPage, ingRec As Integer
Dim strWholeFootnote, strPartFootnote, strRecTxt As String
intPage = Selection.Information(3)
ingRec = ActiveDocument.ActiveWindow.Panes(1).Pages(intPage).Rectangles.Count
strRecTxt = ActiveDocument.ActiveWindow.Panes(1).Pages(intPage).Rectangles(ingRec).Rang e.Text
strWholeFootnote = Len(Selection.Footnotes(iFootnote).Range.Text)
strPartFootnote = Len(Mid(strRecTxt, InStrRev(strRecTxt, Chr(2)) + 1))
If strWholeFootnote > strPartFootnote And InStr(strRecTxt, Chr(2)) = 0 Then
fIsSplit = False
ElseIf strWholeFootnote <= strPartFootnote Then
fIsSplit = False
Else
fIsSplit = True
End If
End Function

Frosty
10-27-2012, 11:03 AM
That function should work... But it requires you to select the footnote somewhere (either in the function or out of it). I don't thin you can get around this basic requirement because the selection while in print layout may be the only way to have the rectangles approach be reliable.

However, you need to give a broader context to what you're trying to achieve. Because I'm pretty sure the rectangles approach is going to slow down too once you start moving your selection around.

When are you performing this function? Some pieces of information are just slow (and get slower) when you're essential asking word for pagination information on large documents.

How you optimize your approach can only be answered by the context of when you're performing the function.

I think the for each loop in my approach might be able to be optimized a bit by checking the first word and the last word... Or some other way of getting that particular info, but optimization can take a long time, and can be a waste of time if you're trying to optimize without the big picture.

gmaxey
10-28-2012, 06:47 AM
I am stabbing in the dark here because I've don't really understand the ultimate goal (footnotes can start looking freaky in multi-column text). Anyway, it seems (to me at least) that if the footnote starts in a rectangle on page(?) and ends in a rectangle on page(?) then it can't be split. So:


Sub Test()
MsgBox fFootNoteIsSplit(1)
End Sub

Public Function fFootNoteIsSplit(ByRef lngFootnote As Long, _
Optional oDoc As Document) As Boolean
Dim oPage As Page
Dim oRec As Rectangle
Dim oFN As Footnote
Dim oFNStart As Word.Range
Dim oFNEnd As Word.Range
Dim bStartonPage As Boolean
Dim bEndonPage As Boolean
Dim lngView As Long
If oDoc Is Nothing Then Set oDoc = ActiveDocument
lngView = oDoc.ActiveWindow.ActivePane.View.Type
If lngView <> wdPrintView Then
oDoc.ActiveWindow.ActivePane.View.Type = wdPrintView
End If
Set oFN = oDoc.Footnotes(lngFootnote)
Set oFNStart = oFN.Range
oFNStart.Collapse wdCollapseStart
Set oFNEnd = oFN.Range
oFNEnd.Collapse wdCollapseEnd
Set oPage = oDoc.ActiveWindow.ActivePane.Pages(oFN.Range.Information(wdActiveEndPageNum ber))
bStartonPage = False
bEndonPage = False
For Each oRec In oPage.Rectangles
If oFNStart.InRange(oRec.Range) Then
bStartonPage = True
Exit For
End If
Next oRec
For Each oRec In oPage.Rectangles
If oFNEnd.InRange(oRec.Range) Then
bEndonPage = True
Exit For
End If
Next oRec
If bStartonPage And bEndonPage Then
fFootNoteIsSplit = False
Else
fFootNoteIsSplit = True
End If
oDoc.ActiveWindow.ActivePane.View.ViewType = lngView
End Function

MacroShadow
10-28-2012, 06:54 AM
Frankly, I'm not sure myself what the end goal is, my colleague didn't explain and now she's on vacation...

Greg you latest seems to work properly and is pretty fast too. Thank You.