Computing the space left on a page.

Hi Guys....

I'm using Office 2010 under Windows 7.


I have a number of mailmerge template documents which I wish to populate using VBA.

Each template starts off with an image (the Company logo), then has the mailmerge fields for the Customer details, then some text, the number of lines in which varies with the template.


I have some VBA code in the template which fires after the mailmerge.

The VBA opens an Excel spreadsheet, reads product data in order to populate various listboxes on a Userform, then allows the User to select a variable number of Products.


All this is working just fine.


My next goal, having saved the selected product details in an array, is to write the array as a list to the bottom of the template document.

Again, I can achieve that quite easily.


The problem is that I'd like to be able to determine when I've got one line left on the page, and more than one Product line still to write.

In that situation I'd like to use the last line on Page 1 to say "(Continued overleaf)", then continue writing the products list on the next page.


In order to achieve this I need to be able to work out exactly how many lines are left on the document before an automatic page break will be triggered.

This means I have to know where my first line will go on the page, i.e. on what line does the basic template's text end.

I've read up about font sizes, and the 120% rule for single spaced text lines, and how to determine the document size, so if I knew which line I was startin on I could work out how many lines were left on the page and act accordingly.


My problem is that I can't seem to find any VBA which will tell me the location of the current end point of the template in terms of the number of lines used, or the distance down the page in points (or any other measurements).


Can any kind soul point me in the right direction? I'm reasonably competent with VBA, but more so in Excel, which seems to have richer functions than thos in Word.


Any assistance would be gratefully appreciated....

I am writing, not because I have an answer but because I have some questions, the answers to which may help someone else give you an answer.

You are talking about a "template" and "mailmerge." Are we talking about a .dotx or .dotm "template" or are you talking about a primary merge document, or perhaps about a .dotx or .dotm primary merge document?

A Word template is not normally used as a primary merge document, but rather as the base for a new document that is the primary merge document.

You are using mailmerge to populate the user information, and vba to populate the order information?

Why do you care about the number of lines? Why not just the end point of the document?
Volunteering to "pay forward" the help I've received in the Microsoft user community.

Charles Kenyon
Sun Prairie, Wisconsin
wordfaq[at]addbalance[dot]com

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

I would suggest that a simple way to ensure that the list is all on one page is to format all the lines except the last as "Keep with next" (assuming each line is a separate paragraph). If all the lines are in a single paragraph, format it as "Keep lines together."

 

This does not address the addition of the "(Continued overleaf)" notice, but is it likely that readers will not notice that there is more on the back of the page?

Microsoft MVP (Word) since 1999
Fairhope, Alabama USA
http://ssbarnhill.com
http://wordfaqs.ssbarnhill.com
Screen shots captured with TechSmith's Snagit

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

Let me suggest a solution that doesn't change your macro or involve any calculations.

In the footer of the template (or main merge document or whatever), insert the following nested field:

{ IF { PAGE } < { NUMPAGES } "(Continued overleaf)" "" }

The curly braces must be inserted by pressing Ctrl+F9, not by typing them.

When the merged document is only one page, this field won't display anything. When it overflows to a second (or third...) page, it will display the continuation line.

_____________________________
https://jay-freedman.info

1 person found this reply helpful

·

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

Charles and Suzanne....


First, thank you both for your thoughtful replies.


To answer - the mailmerge is functionality used by a further third party product ("C4W") to create documents, and allows ".dotx" or  ".dotm" templates to populate various fields with reference to data held in the database of the third party product. VBA is perfectly permissible in C4W template documents.


I am trying to create a document similar to a "materialsto use"  quote. The User selects the equipment necessary to fulfil the requirements on behalf of the Customer, which could be extensive, and in all but the simplest form will certainly span at least one page, and possibly more, which is why the "Keep together" option may not be appropriate.


When the User dismisses the Userform used to select the equipment, I know how many lines are to be added to the template document, and as I can get the size of the document in VBA I can loop through the array of equipment items, pausing as appropriate to issue the "(Continued overleaf)" line.

However, in order to achieve this using the same VBA for differently headed documents, it is vital to know where on the page I am starting.


I may have found the basis for an answer in the following properties -

ThisDocument.Paragraphs.Last.Range.Information(wdVerticalPositionRelativeToPage), & 
ThisDocument.Paragraphs.Last.Range.Information(wdVerticalPositionRelativeToTextBoundary)

which are looking promising, giving the current documents end position in "points" down the page.


Should this prove to be fruitful I will confirm with a further post....

Steve

"There's nowt so rare as Common Sense"

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

Will that work? For some reason I'm envisioning this as a Letter-type merge, in which case SECTIONPAGES would be required rather than NUMPAGES.
Microsoft MVP (Word) since 1999
Fairhope, Alabama USA
http://ssbarnhill.com
http://wordfaqs.ssbarnhill.com
Screen shots captured with TechSmith's Snagit

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

Thanks for everyone's help - I may have a working solution which I'll post the code for tomorrow....
Steve

"There's nowt so rare as Common Sense"

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

Will that work? For some reason I'm envisioning this as a Letter-type merge, in which case SECTIONPAGES would be required rather than NUMPAGES.

Yes, you're right.
_____________________________
https://jay-freedman.info

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

Looks like a gloomy end to 2013!

 My initial optimism at what looked like a solution is misplaced.

In case any kind soul can see my logic errors / bad assumptions, I've copied the VBA from my testbed below. It is designed to generate a random number of lines with a random font size, then work out how many lines would fill the remainder of the page, and write them.

"Close, but no cigar!"


Happy New Year to all!


Option Explicit
Public Sub TestLines()
Dim intLinesToFooter        As Integer
Dim intFontSize             As Integer
Dim intPtr                  As Integer
Dim intTopLines             As Integer
Dim rngDoc                  As Range
'*
'** Delete current contents of the document.
'*
  Set rngDoc = ActiveDocument.Content
  rngDoc.Delete
'*
'** Generate two random numbers -
'** first, the number of of "top" lines (0 to 20),
'** and secondly the font size (8 to 22),
'** then write them to the top of the document.
'*
  Randomize
  intTopLines = CInt(Int(20 * Rnd()))   'Between 0 and 20
  intFontSize = CInt((Int(15 * Rnd()) + 8)) 'Between 8 and 15.
 
  Call MsgBox("Using " & intTopLines & " at font size " & intFontSize)
 
  ActiveDocument.Content.Font.Name = "Arial"
  ActiveDocument.Content.Font.Size = intFontSize
 
  With rngDoc
    intPtr = 1
    Do While intPtr <= intTopLines
      .InsertAfter "Top line " & intPtr & " of " & intTopLines & vbCrLf
      With .ParagraphFormat      'Set general paragraph properties.
        .SpaceBefore = 0
        .SpaceAfter = 0
        .Space1
      End With
      intPtr = intPtr + 1
    Loop 'intPtr
   
    intLinesToFooter = funLinesToFooter(CSng(intFontSize))
   
    Call MsgBox("Lines remaining = " & intLinesToFooter)
   
    intPtr = 1
    Do While intPtr <= intLinesToFooter
      .InsertAfter "Line " & intPtr & " of " & intLinesToFooter & vbCrLf
      With .ParagraphFormat      'Set general paragraph properties.
        .SpaceBefore = 0
        .SpaceAfter = 0
        .Space1
      End With
      intPtr = intPtr + 1
    Loop 'intPtr
  End With
  ActiveDocument.Select
End Sub
Public Function funLinesToFooter(Optional sngLineHeight As Single = 12) As Integer
Dim sngBodySize             As Single
Dim sngFooterSize           As Single
Dim sngHeaderSize           As Single
Dim sngLineHeightAdj        As Single
Dim sngPageSize             As Single
Dim sngRemainderSize        As Single
Dim sngVerticalPos          As Single
  sngLineHeightAdj = 120 * (sngLineHeight / 100)
  With ThisDocument
    With .PageSetup
      sngHeaderSize = .HeaderDistance
      sngFooterSize = .FooterDistance
      sngPageSize = .PageHeight
    End With
    sngVerticalPos = .Paragraphs.Last.Range.Information(wdVerticalPositionRelativeToTextBoundary)
  End With
  sngRemainderSize = (sngPageSize - _
                      (sngHeaderSize + sngFooterSize)) - _
                      sngVerticalPos
'*
'** The following computation assumes that a line takes up
'** 120% of the size of the font.
'*
  funLinesToFooter = sngRemainderSize / sngLineHeightAdj
End Function

Steve

"There's nowt so rare as Common Sense"

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

On the basis that there's always a way round things, I now have a work around which approaches the problem in a different way.

If anyone has a similar problem, the prototype for inelegant and ham-fisted way around it is given below. I know the functionality should (and will) be rearranged, but the basic idea is to track the internal page numbers, and when the page number increments, you've just gone one line over the page.

We should then be able to delete the overspill line, delete the last line on the previous page, then write the "(Continued)" text (which will become the new last line and cause a page throw), and then start writing again, commencing with the last (true) line from the previous page.

I'm not quite sure why the "(Continued)" line needs a preceding vbCrLf, but other than that it seems quite logical. I'll have another think when I'm not quite so tired!


Thanks again to all who offered help or comments.....


Code and testbed follows


Option Explicit
Dim rngDoc                  As Range
Public Sub TestLines()
Dim arrBotLines()           As String
Dim intLinesToFooter        As Integer
Dim intFontSize             As Integer
Dim intPtr                  As Integer
Dim intBotLines             As Integer
Dim intTopLines             As Integer
Dim lngCurrentPageNo        As Long
Dim lngLastPageNo           As Long
'*
'** Delete current contents of the document.
'*
  Set rngDoc = ActiveDocument.Content
  rngDoc.Delete
'*
'** Generate three random numbers -
'**   1. The number of of "top" lines (0 to 20),
'**   2. The font size (8 to 22),
'**   3. The number of lines to be added (20 to 100)
'** then write them to the top of the document.
'*
  Randomize
  intTopLines = CInt(Int(20 * Rnd()))           'Between 0 and 20
  intFontSize = CInt((Int(15 * Rnd()) + 8))     'Between 8 and 22.
  intBotLines = CInt((Int(81 * Rnd()) + 20))    'Between 20 and 100.
 
  Call MsgBox("Creating " & intTopLines & " Top lines and" & vbCrLf & _
              intBotLines & " Bottom Lines" & vbCrLf & _
              "at font size " & intFontSize)
 
  ReDim arrBotLines(intBotLines - 1)
  For intPtr = 0 To intBotLines - 1
    arrBotLines(intPtr) = "Bottom line " & intPtr + 1 & " of " & intBotLines
  Next intPtr
 
  ActiveDocument.Content.Font.Name = "Arial"
  ActiveDocument.Content.Font.Size = intFontSize
 
  intPtr = 1
  Do While intPtr <= intTopLines
    Call WriteLine("Top line " & intPtr & " of " & intTopLines)
    intPtr = intPtr + 1
  Loop 'intPtr
   
  lngLastPageNo = ActiveDocument.Paragraphs.Last.Range.Information(wdActiveEndPageNumber)
   
  intPtr = 0
  Do While intPtr <= UBound(arrBotLines)
    lngCurrentPageNo = ActiveDocument.Paragraphs.Last.Range.Information(wdActiveEndPageNumber)
    If lngCurrentPageNo > lngLastPageNo Then
'*
'** This delete removes the first line on the next page.
'*
      ActiveDocument.Paragraphs.Last.Range.Delete
'*
'** This delete removes the last line on the previous page.
'*
      ActiveDocument.Paragraphs.Last.Range.Delete
      intPtr = intPtr - 1       'Causes the "overspill" line to be rewritten.
      Call WriteLine(vbCrLf & "(Continued)")
      lngLastPageNo = lngCurrentPageNo
    End If
    Call WriteLine(arrBotLines(intPtr))
    intPtr = intPtr + 1
  Loop 'intPtr
End Sub
Public Sub WriteLine(strText As String)
  With rngDoc
    .InsertAfter strText & vbCrLf
    With .ParagraphFormat      'Set general paragraph properties.
      .SpaceBefore = 0
      .SpaceAfter = 0
      .Space1
    End With
  End With
End Sub

Steve

"There's nowt so rare as Common Sense"

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

 
 

Question Info


Last updated April 14, 2025 Views 287 Applies to: