Identify page on which a bookmark is located

B

BruceM

I have code (obtained from this group in response to an earlier question) to
print only the part of a document between two bookmarks. The following
works inconsistently. Details are after the code. The code itself is in an
Add-In.

Dim x1 As Long ' start character of bookmark
Dim x2 As Long ' end character of bookmark
Dim p1 As Long ' start page
Dim p2 As Long ' end page

Dim rTmp As Range
Set rTmp = ActiveDocument.Range
' Allow for a bookmark at the beginning of document
If rTmp.Bookmarks("DocStart").Range.Start = 0 Then
x1 = 1
Else
x1 = rTmp.Bookmarks("DocStart").Range.Start
End If
x2 = rTmp.Bookmarks("DocEnd").Range.End
p1 = rTmp.Characters(x1).Information(wdActiveEndPageNumber)
p2 = rTmp.Characters(x2).Information(wdActiveEndPageNumber)

ActiveDocument.PrintOut _
Range:=wdPrintFromTo, From:=CStr(p1), To:=CStr(p2)

Set rTmp = Nothing

When the bookmarks DocStart and DocEnd are on the same page (so that only
one page is printed, which is the case most of the time) it seems to work as
intended. However, in other situations the results are mixed at best. For
instance, DocStart is on page 7, but p1 is 9 when I step through the code.
In the same document, if DocEnd is on page 8, it is identified as 11. That
is, the variables p1 and p2 are 9 and 11 rather than 7 and 8.
Another problem I have noted is that I cannot find a place for the DocEnd
bookmark in a two-page document (I want to print both pages) that is not
identifed as either page 1 or page 3. However, if I use the Word Count
toolbar to count the number of characters, and replace x2 with that number
(e.g. 2408), this line properly identifies p2 as 2:
p2 = rTmp.Characters(x2).Information(wdActiveEndPageNumber)

If I do not manually set x2 to 2408, it is identified as something like 4500
(which is more than the total number of characters in the document), and
that line of code throws the following error:
5941 - The expected member of the collection does not exist

That is all the information I have been able to obtain. The problem seems
to be the same whenever it occurs: the character position of the bookmarks
(the variables x1 and x2) are not identified properly.

If I could identify the page on which each of the bookmarks is located, I
could get the code to do what is needed. If this is not possible, I could
settle for setting the variables p1 and p2 manually for documents in which
problems occur, and have the code above start by testing whether the
variables already have a value, and using that value instead of the values
generated by the code, or something like that. I would rather not do that,
but I can accept a workaround if necessary.

I am using Word 2003. If it matters, the code above is designed to include
the header and footer in the printout. Other code with which I experimented
did not print the header and footer. It is better by far if I can include
the header and footer.
 
R

Russ

Bruce,
Three things may trip you up:
1.Word usually determines page count from the current default printer
driver.
2.Each section between section breaks may have different Page Setup
formatting. So you may also have to include the section numbers that your
starting bookmark and ending bookmark are in.
3.Shown Hidden text (Index fields, etc.) may alter what .Information... may
see versus what Print Preview sees, if the option to not print hidden text
is on. If that is the case, then use the not show hidden text toggle before
using .Information...

Dim blnShowAll As Boolean
blnShowAll = ActiveWindow.ActivePane.View.ShowAll
ActiveWindow.ActivePane.View.ShowAll = False
....most of your code
ActiveWindow.ActivePane.View.ShowAll = blnShowAll
 
B

BruceM

Thanks for the reply. As it happens the ShowAll did not accomplish what was
needed (it still gave an incorrect number for the character position of the
second bookmark. However, as I investigated further I think I came up with
something. So far it seems to work in all situations.

Dim p1 As Long ' start page
Dim p2 As Long ' end page
Dim AllChar As Long ' Section character count

Dim rTmp As Range
Dim intSect As Integer

' Go to the first bookmark
Selection.GoTo What:=wdGoToBookmark, Name:="DocStart"

' Find the number of the section containing the bookmark
intSect = Selection.Information(wdActiveEndSectionNumber)
' Count the characters in the section
AllChar =
ActiveDocument.Sections(intSect).Range.ComputeStatistics(wdStatisticCharactersWithSpaces)
Set rTmp = ActiveDocument.Sections(intSect).Range
' At the first character in the section, find the page number
p1 = rTmp.Characters(1).Information(wdActiveEndPageNumber)
' At the last character in the section, find the page number
p2 = rTmp.Characters(AllChar).Information(wdActiveEndPageNumber)

ActiveDocument.PrintOut _
Range:=wdPrintFromTo, From:=CStr(p1), To:=CStr(p2)

Set rTmp = Nothing

This assumes the printout is all of one section. I expect the code could be
adapted for printing multiple (including non-contiguous) sections. I think
I can avoid those situations, but if not, I think there is a way to work
with it.
 
B

BruceM

It seems I spoke too soon. In some situations the last page is not
identified correctly. I tried adding your code (ShowAll), but it had no
effect.
I tried inserting another bookmark and finding the page as I did with the
starting bookmark, but no good. It is as if the second page doesn't exist.
To test, I deleted everything except for the 2-page section I intended to
print. The character count that I got by using the Word Count toolbar was
the same as the one I got for AllChar using the code, but p2 in the code was
still 1, even though the last character was clearly on the second page.
The strange part is that it usually works properly, but then on a very
similar document it doesn't work. When it doesn't work, there is nothing to
suggest why the two similar documents behave differently.
I have found that if I add characters to the second page I can eventually
get Word to understand that the end of the section is on the second page.
Maybe I will need to add a few hundred spaces or something when I run into a
problem, but I like to think there is something more elegant than that.
 
R

Russ

Bruce,
Some quick observations.
I would use
wdActiveEndAdjustedPageNumber
For getting the end of the ranges. That automatically adjust page numbers to
any page number manipulation going on in the document.
------------------------------------
I'm not seeing why you are involving characters?

Basically the pseudocode is:
Set a new docstart2 range = to docstart.start, if docstart is not an
insertion point and you don't want to collapse it to its start.
Set a new docend2 range = to docend.end, if docsend is not an insertion
point and you don't want to collapse it to its end.

Find wdActiveEndAdjustedPageNumber of those insertion points.
(Optional, find wdActiveEndSectionNumber of those insertion points.)

Use that information in your printout command line.
 
B

BruceM

I have not had a chance to try out your suggestions, and I am about to leave
for the day, but I want to thank you for replying. I will try implementing
your suggestions tomorrow.
BTW, I used characters because I was blundering around and stumbled upon
that, and it seemed to work. Actually, there is only one document so far
where it fails, but I wouldn't be surprised if there are others. Clearly it
is not all that reliable.
 
B

BruceM

Thanks again for hanging in there on this issue. After posting (but before
receiving your reply) I tried wdActiveEndAdjustedPageNumber, but it did not
change the outcome on that one document that was giving me trouble.
However, as I become more familiar with the options I found something else
that seems to work:

Dim p1 As Long ' Start page
Dim p2 As Long ' End page

Selection.GoTo what:=wdGoToBookmark, Name:="DocEnd"
p2 = Selection.Information(wdActiveEndAdjustedPageNumber)
Selection.GoTo what:=wdGoToBookmark, Name:="DocStart"
p1 = Selection.Information(wdActiveEndAdjustedPageNumber)

ActiveDocument.PrintOut _
Range:=wdPrintFromTo, From:=CStr(p1), To:=CStr(p2)

Set rTmp = Nothing

I don't understand what you mean by "insertion point" in this context.
Also, in my research I came across references to "collapsing". Because of
my lack of understanding I do not have a view one way or the other about
whether I "want to collapse it to its start". Another thing is that I could
not figure out how to use the Start property. In my attempts to use it I
got a type mismatch compile error. VBA Help documentation is spotty at
best, so I can't figure out how to resolve that (or for that matter just how
to use Start in the first place).

Any further insights (or links to explanations) would be appreciated, but in
particular does it seem that the code above will address the problem?
Again, I may want to print just page 1, or page 1 and 2, or page 7, or other
combinations.
 
R

Russ

Bruce says:
I don't understand what you mean by "insertion point" in this context.

The insertion point is the thin-blinking cursor position mark where you are
typing text. Selection can be a selected word of text, a selected paragraph
of text, etc.; or it can be no text selected but only the current cursor
position or insertion point. I avoid using the term Œhighlighted¹ for
selected text, because Word provides highlight colors to truly highlight
text, like a highlighter marker on paper. Selected text is text with
temporarily reversed colors for font and background to make it obvious to
the user what is selected and can be manipulated.
Also, in my research I came cross references to ³collapsing². Because of my
lack of understanding I do not have a view one way or the other about whether I
³want to collapse it to its start².

Here is what VBA Help says about the Collapse method:
=======Quote
Collapses a range or selection to the starting or ending position. After a
range or selection is collapsed, the starting and ending points are equal.

Syntax

expression.Collapse(Direction)

expression Required. An expression that returns a Range or Selection
object.

Direction Optional Variant. The direction in which to collapse the range or
selection. Can be either of the following WdCollapseDirection constants:
wdCollapseEnd or wdCollapseStart. The default value is wdCollapseStart.

Remarks

If you use wdCollapseEnd to collapse a range that refers to an entire
paragraph, the range is located after the ending paragraph mark (the
beginning of the next paragraph). However, you can move the range back one
character by using the MoveEnd method after the range is collapsed, as shown
in the following example.

Set myRange = ActiveDocument.Paragraphs(1).Range
myRange.Collapse Direction:=wdCollapseEnd
myRange.MoveEnd Unit:=wdCharacter, Count:=-1

======UnQuote

Collapsing may only matter if your bookmarks are not just insertion points.
If they include text, then it depends on whether you want to include the
Start and End bookmarks ³full text² in the range of pages to print?
Collapsing the current selection changes the current selection to an
insertion point, either at the beginning or end or the previously selected
text.
So in order not to include both bookmarks text, you would collapse the
Start-bookmark to its end (then find the adjusted page number) and the
End-bookmark collapsed to its start (then find the adjusted page number).
Another thing is that I could not figure out how to use the Start property. In
my attempts to use it I got a type mismatch compile error. VBA Help
documentation is spotty at best, so I can't figure out how to resolve that (or
for that matter just how to use Start in the first place).

Here¹s one thing it says about the .Start property:
=======Quote
Returns or sets the starting character position of a selection, range, or
bookmark. Read/write Long.
=======UnQuote
Notice it says you can read or write to it, but then it says Long, which
means it is of a type number. The .Start and .End properties return a number
indicating how many characters from the document beginning, which is at
..Start = 0 and .End = 0 or ActiveDocument.Range(0,0).

Here¹s one thing it says about the .Information property:
=======Quote
Returns information about the specified selection or range. Read-only
Variant.
=======UnQuote
Notice it is read only and returns information about a type selection or a
type range input. (not a type number input, as what is returned by the
..Start property; hence your type mismatch error.)


What this boils down to is, by collapsing a range or selection to the start
or end it still remains a selection type or range type and can be used as
input to the .Information property. However you may not need to collapse to
the end because, by default, the
Selection.Information(wdActiveEndAdjustedPageNumber) returns the page number
on which the *ActiveEnd* of the selection ends.

Why use adjusted page argument - wdActiveEndAdjustedPageNumber?
Because, for instance, if the document is divided into different Word
sections with section breaks, each section can do its numbering differently,
i.e. starting the page numbers counting from the number one again. And if
your bookmarks span across different sections and you gathered the adjusted
page number and the section information with wdActiveEndSectionNumber, you
could ,for example, build up and use the string ³p2s2-p3s5² within the
..PrintOut method as shown below in the quoted Word help to print a range of
pages from Page 2(Section 2) to Page 3(Section 5).

From regular Word Help:
=========Quote
Print specific pages and sections

You can print specific pages, one or more sections, or a range of pages in
more than one section. On the File menu, click Print.

To print In the Pages box
Noncontiguous pages Type the page numbers with commas between
them. Type the range of pages with a hyphen between the starting and ending
numbers in the range. For example, to print pages 2, 4, 5, 6, and 8, type
2,4-6,8
A range of pages within a section Type p page number s section
number. For example, to print pages 5 through 7 in section 3, type p5s3-p7s3
An entire section Type s section number. For example, type s3
Noncontiguous sections Type the section numbers with commas
between them. For example, type s3,s5
A range of pages across sections Type a range of page numbers and
the sections that contain them with a hyphen between the starting and ending
numbers in the range. For example, type p2s2-p3s5
=========UnQuote

To see if you needed to build up a more ³complex² print page string, you
would use ³if statements² to test if the bookmarks were in different
sections.

Choice between using Range or Selection?
Nowadays, the preferred method is to use Ranges for speed since Selection
requires Word to graphically indicate where the current selection is located
and move it, if it changes.

Logically, the Range speed should have worked that way from the beginning.
It appeared that Word 97 had a bug in it that made Ranges slower than
Selection, but it was changed in the next version of Word to make Ranges as
speedy as they should have been.
Selection still seems to work faster in Tables than Ranges at this time.
Someone suggested it was because of the way Table structures are arranged in
memory.

Almost everything that can be defined by Selection can be defined by Range.
When you record a macro, the code usually shows Selection because manually
you must select objects to indicate what you want to apply actions upon. But
most of the time you can trim the resultant, recorded code of the
unnecessary options and change the Selections to Ranges for speed.

In your code, in order to use Ranges and not have to use the Goto Bookmark,
which Selects the Bookmark text, maybe requiring you to Collapse the
Selection; you could use Ranges like this to find out which page the
Previous Character to the First Character of the bookmark named 'End' Range
is located or the Next Character after the Last Character of the bookmark
named 'Start' Range is located:
ActiveDocument.Bookmarks("End").Range.Characters.First.Previous.Information
(wdActiveEndAdjustedPageNumber)
ActiveDocument.Bookmarks("Start").Range.Characters.Last.Next.Information
(wdActiveEndAdjustedPageNumber)
or
ActiveDocument.Bookmarks("Start").Range.Information
(wdActiveEndAdjustedPageNumber)
Since, by default, .Information (wdActiveEndAdjustedPageNumber) gives you
where the end of the range or selection is located.
 
B

BruceM

Wow! Thanks for all of the information. I think I understand now that I
may have to proceed differently if the bookmark text rather than a point in
the document. I know what an insertion point is in Word, but it seems I
over-thought it in a VBA context.

For some reason my Help file contains none of the information about methods
and properties you have quoted, at least not by searching for the specific
information (that is, "Collapse" produces nothing in VBA Help). Maybe I
need to install SP3. For some reason that was not included with my
automatic updates. Or maybe the information appears under another heading.
My general experience with Help is that the information is often there, but
it is so poorly indexed it may as well not be.

Thanks too for clearing up Sections and Ranges, and for pointing out the way
to build a print string consisting of non-contiguous pages, just as that can
be done with the Print dialog. I hope I don't need to do that for this
project, but it is good to know I can.

I substituted your code:
ActiveDocument.Bookmarks("End").Range.Characters.First. _
Previous.Information(wdActiveEndAdjustedPageNumber)
ActiveDocument.Bookmarks("Start").Range _
.Information (wdActiveEndAdjustedPageNumber)

for my GoTo Bookmark code. Since the bookmarks were insertion points, as I
understand it there was no need to collapse anything, so I think my system
was OK, but this is more compact, which is my choice when all else is equal.
Your explanation of how the page number is determined is very helpful, as is
your explanation for the Type Mismatch error. I have saved this thread to
my personal Help files.

Thanks again for all of your help and explanation. As I mentioned, I don't
know why I don't have the information in VBA help, but the fact is I cannot
find it, so your quoted information has been quite valuable.
 
B

BruceM

I knew about that Alt + F11 from Access, but I keep forgetting about F1.
Just now I took another route into Help (via drilling down through Microsoft
Word Visual Basic Reference). I found Properties, selected "I", and clicked
"Information Property", only to be told the Help feature is not installed.
I installed it, and now I seem to have the complete library. However, I was
able to find some information in Help before installing the feature. Maybe
the fragmentary information was in Office VB Reference, or maybe VB
Documentation, but in any case it is pretty bewildering that I can search
for a topic and be told there is no information on the topic rather than
that Help is not installed. Then again, that is a lot to expect from
Microsoft.
BTW, the code has identified correctly the start and end pages of the print
range in every document so far. Thanks again.
 
R

Russ

Bruce,
Sounds like you have the VBA Help sorted out.

Regarding code:
My only concern, as I think more about it, is that I didn't test it for
bookmarks at the very beginning or end of a document, since it looking for a
previous character or next character and there are none at the beginning and
end respectively. You could drop the .previous or .next. And even the
Characters.first and Characters.last if used, if your delimiting bookmarks
will always be insertion points.
 
B

BruceM

Thanks for the additional observations. The DocEnd bookmark will not be at
the very end of the document for reasons having to do with the structure of
the documents. Even if it is I don't think it will matter, since Previous
rather than Next is being used there. The DocStart bookmark is identified
by the code variation you suggested, so neither Previous or Next apply.

p1 =
ActiveDocument.Bookmarks("DocStart").Range.Information(wdActiveEndAdjustedPageNumber)
p2 = ActiveDocument.Bookmarks("DocEnd").Range.Characters _
.First.Previous.Information(wdActiveEndAdjustedPageNumber)

If I am reading this correctly, the bookmark DocStart is the Range used for
p1, so wdActiveEndAdjustedPageNumber will find the page number at the end of
the range. The bookmarks are intended to be insertion points, but even if
they are not I don't see the end of the bookmark being on a different page
from the beginning of the bookmark.
As I understand, p2 is derived by going to the range represented by the
DocEnd bookmark, going to the character before the first character in the
range, and finding the page number there. Again, I see no problem with that
as long as DocEnd is not at the very beginning of the document or of a page
that is meant to be included in the print range. DocEnd is intended to be
an insertion point, but even if it is not I expect Previous can be
eliminated.
Of course, all of this is based on the assumption that I understand what is
going on with the code.
 
R

Russ

Bruce,
It does seem, by what you wrote, that you do understand correctly what the
code is doing.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top