PDA

View Full Version : Solved: How to avoid printing blank pages?



thushan
08-16-2005, 04:11 AM
Although the problem sounds simple, it becomes complex when you run your macro on ugly looking huge docs that contain more section breaks than pages and some very weird formatting.

(Note: When identifying blank pages, we can ignore headers and footers)

I have attached a sample file with my maco (PrintGoodPages).
(But I would very much like you all to think afresh rather than looking at my code, because I've been trying various versions of the same macro and seems to be in a rut... :( )

Actual blank pages of the doc are 5,7,10,12,35.

But this macro has an obvious bug, namely it thinks page 14 is blank. But it's not. The reason is I use the asc value 12 to identify a page break. But asc 12 is also used as a Section breaks.

Question 1: How to differentiate between a page break asc 12 and a section break asc 12? Or is there a better way to identify a blank page?

The second bug is it prints the 36th page twice and drops the 37th page?
The reason is, the macro can't go to page 37.

Question 2: Go to page 36. Press Next Page button (bottom right hand corner) or presss Ctrl+PgDown. It doesn't go to page 37!!
Or try the Go To dialog and put 37... you can't go there! In other words, programatically you can't go to page 37. Is this a MS Word bug or is the doc currupted?

Thank you for any info...

Best regards,
Thushan

fumei
08-16-2005, 06:48 AM
I will look at the document, but I can answer question 1.

True, both a page break and a Section break are Chr(12). However, the way to tell is to:

1. go to the Range location just before that break;
2. collapse the range
3. pick up the section index number
3. expand the END to capture the next character (the break)
4. check to see if that character is Chr(12)
5. if it IS, then pick up the section number (the index) of the range location;
6. compare the first location number (it is a long integer) with the second. If the second is greater - it is a new section, and therefore the Chr(12) is a Section break. if it is NOT greater then the Chr(12) is a page break, not a section break.

The following was code for another thread regarding actions to occur on the LAST page. So the other part of the code went to the top of the last page, backed up ONE character (the last character of the previous page), then ran the functions to see what caused the break into the last page.

To have this work for any page the Selection is on, try:

NOTE: ActiveDocument has been replaced by aDoc as a Document object. Unless you do the same, replace aDoc with ActiveDocument
Sub WhatIsNextBreak()
' set selection to be the last character of the page
' the Selection is CURRENTLY on
Selection.SetRange _
Start:=aDoc.Bookmarks("\page").Range.End - 1, _
End:=aDoc.Bookmarks("\page").Range.End - 1
' check to see if the NEXT character (the break)
' is Chr(12)
If checkchar = True Then
If checkDifferentSection = True Then
Msgbox "The next page is caused by " _
& "a Section Break."
Else
Msgbox "The next page is caused by " _
& "a page break."
End If
Else
Msgbox "The next page is caused by text " _
& "extending beyond the available limits of this " _
& "page - a normal page break."
End If
End Sub

Function checkchar() As Boolean
' check if the character between
' second last page, and LAST page is Chr(12)
' a Section break and and a
' Ctrl-Enter page break are BOTH Chr(12)
With Selection
.Collapse direction:=wdCollapseEnd
.Expand Unit:=wdCharacter
If Right(Selection.Text, 1) = Chr(12) Then
checkchar = True
Else
checkchar = False
End If
.Collapse direction:=wdCollapseStart
End With
End Function

Function checkDifferentSection() As Boolean
' checks if section number is different
' between second last page and LAST page
Dim iSectionA, iSectionB As Integer

With Selection
.Collapse direction:=wdCollapseEnd
iSectionA = Selection.Sections(1).Index
.MoveRight Unit:=wdCharacter, Count:=1
iSectionB = Selection.Sections(1).Index
End With
If iSectionB > iSectionA Then
checkDifferentSection = True
Else
checkDifferentSection = False
End If
End Function

TonyJollans
08-16-2005, 07:31 AM
A bit of a mess, isn't it?

To be perfectly honest, manually removing the blank pages seems the best bet - you are not printing page numbers so nothing gets out of sequence. Unless you have lots of documents like this it isn't worth trying to automate anything.

This won't help you much at the moment but, for information, what happens when you go to page 37 is that you go to the first cell in the first row of the table at the start of page 37 and that cell starts on page 36.

fumei
08-16-2005, 08:43 AM
The document has some odd things. A hard page break following a Section break (Continuous), followed by a Section break Next page.

Perhaps if these were investigated? I agree with Tony, it would better to try and eliminate the blank pages.

Oh, and thushan, you may want to go back to your original thread vis-a-vis absolute page numbers, and take a look. I posted the internal code that Word uses to put those "absolute" numbers in the status line. The calculation code to get those numbers. As again, there are no absolute page numbers in Word.

thushan
08-17-2005, 09:47 AM
The docs are created using a program which merges some other docs and inserts some data from a database. Modifying the program is out of the question. The only option is to get rid of blank pages.

About the problem of going to 37th page.
I created a similar doc (which is attached) with one cell spanning two pages and there you can go to next page using Next page button (or Ctrl + PgDown). So it can't be a Word bug. Or can it? Or can it be that the doc is currupted?

Thank you for your responses.

Thushan

mdmackillop
08-17-2005, 12:56 PM
A suggested methodology, but I haven't found the appropriate properties or worked out any code.
Selection.MoveDown line by line, returning the number of the current page. If that number is returned only once, it is a page not to be printed.

TonyJollans
08-17-2005, 01:12 PM
Hi thushan,

You have a very complicated setup including many merged cells.

I have amended your test doc to replicate the problem - see attached file.

TonyJollans
08-17-2005, 01:43 PM
Perhaps a bit more detail would help.

This isn't really a bug. It's just the way Word works.

There are some limitations when working with complex tables - and you have a very complex table.

You have merged cells, merged again, with rows which are not allowed to break across pages, giving a 'multi-row cell' in column 1 which spreads across pages 36 and 37, with many corresponding cells in what for simplicity we will call column 2. One of the cells in column 2 is the first one on page 37. Word finds the start of that cell, but the corresponding cell in column 1 is on page 36 and that is where Word 'goes to' believing (with some justification) that as it is at the start of a cell, and wants the start of the page that it should go to the start of the cell in the first column of the row.

In your simple sample document you have text in column 2 which flows over the page boundary. Word recognises that the page starts in the middle of a cell and so doesn't try and adjust back to the start of the row.

I hope that makes it a little clearer. You really are going to have a tough time sorting this lot automatically. Is it really completely out of the question to modify the program which creates these monsters?

mdmackillop
08-17-2005, 02:49 PM
The following lists the pages not to be printed (at least on this sample)
Option Explicit
Public Sub DoCount()
Dim Lines()
Dim Pages As Long, Pg As Long, s As Long, Count As Long
Dim i As Long, Ln As Long
Dim Msg As String

ReDim Lines(50)
Application.ScreenUpdating = False
Selection.HomeKey Unit:=wdStory
Pages = Selection.Information(wdNumberOfPagesInDocument)
Pg = 1
i = 1
Do
Selection.MoveDown Unit:=wdLine, Count:=1
s = Selection.Information(wdActiveEndPageNumber)
'Count lines on each page and save in array
If s = Pg Then
Count = Count + 1
Lines(i) = Count
'Reset when page changes
Else
Count = 0
Pg = Pg + 1
i = i + 1
End If
Loop Until s = Pages
ReDim Preserve Lines(i)
'Get pages where line count is less than 3 (adjust to suit)
For Ln = 1 To i - 1
If Lines(Ln) < 3 Then Msg = Msg & Ln & ", "
Next
Msg = Left(Msg, Len(Msg) - 2)
Application.ScreenUpdating = True
MsgBox "Pages to skip: " & Msg
End Sub

thushan
08-17-2005, 08:43 PM
Wow mdmackillop (http://www.vbaexpress.com/forum/member.php?u=87)! You are a genius! ;)
You solved my Question number 2.
With your algo, it moves from page 36 to page 37 smoothly. Now try to explain how that happens! :)

There's one thing I don't understand though. What's the logic behind identifying a page as blank, if the line count is less than 3?

But your algo is not perfect in the sense that it does not identify pages as blanks if the page contain only several empty lines (maybe with spaces) or if it has only several continuous section breaks. I think this is mainly due to the fact that I didn't explicitly define a blank page. Blank Page: A page that does not contain any visible characters when printed. But this is a minor issue which I can handle myself.

Thanks again mdmackillop (http://www.vbaexpress.com/forum/member.php?u=87).

Thushan

mdmackillop
08-17-2005, 11:34 PM
My code steps down each line, returning and counting the occurences of each page it is on. One of the pages had two "breaks", hence two steps and so I needed <3 to return a correct answer for this sample. I can't guarantee there will not be blanks with more breaks or very short pages which may "slip through".
Page 37 (the last page) is listed as a non-printing page as the code exits when that page number is reached. I assumed the last page would always be printed, so cut the last item from my array.
The code has been made to suit the sample so a bit of trial and error I'm afraid.

You could also consider counting characters between line positions, which might also be suitable for determining a "blank" page.

TonyJollans
08-18-2005, 06:38 AM
The more I look at this document, the more I want to scream.

Amongst other horrors, your pages are of differing lengths yet you don't seem to want to use different printers or paper trays.

Anyway, it seems to me that your problem is really caused by empty Sections (sometimes Next Page, sometimes Continuous but with Page Setups forcing next page). If you can identify the sections to print you'll have more success than trying to work with the pages.

I can't be sure about all your documents but this seems to work for the sample one:

For Each Sect In ActiveDocument.Sections
If Sect.Range.End - Sect.Range.Start <> 1 Then _
SectsToPrint = SectsToPrint & ",s" & Sect.Index
Next
Application.PrintOut Range:=wdPrintRangeOfPages, Pages:=Mid$(SectsToPrint, 2)

thushan
08-31-2005, 10:17 AM
Hi all,
Thank you all for your help.
And sorry about the delayed reply. Had to get some stuff tidied up.
I finally managed to write a macro that prints only non blank pages.
It uses pNsN format and ranges to print.
I am posting the macro here with.

Thank you again,
Thushan


' Copyright 2005 Standard Solutions Inc - All Rights Reserved
' Author: Thushan Abeysekera
' Date: 2005/08/31
' Description: PrintGoodPages macro prints only non blank pages.
Public Sub PrintGoodPages()
Dim docType
Dim currentPageNo As Long, currentLineNo As Long
Dim sRelativePage As String, sSection As String, sCurrentPage As String
Dim sSectionsNPages As String, sTempSectionsNPages As String, testAsc As String
Dim sStartOfRange As String, sEndOfRange As String
Dim bInclude As Boolean, bAbnormalNextPage As Boolean, bStartOfRange As Boolean
Dim Pgs As Long, i As Long, j As Long, k As Long
Dim sSectionsNPagesArr(1 To 10) As String
docType = ActiveWindow.View.Type
currentPageNo = Selection.Information(wdActiveEndPageNumber)
currentLineNo = Selection.Information(wdFirstCharacterLineNumber)
ActiveWindow.View.Type = wdPrintView
sSectionsNPages = ""
sSection = ""
sRelativePage = ""
sCurrentPage = ""
sStartOfRange = ""
sEndOfRange = ""
bInclude = False
bAbnormalNextPage = False
bStartOfRange = True
i = 0
j = 1
k = 1
Pgs = ActiveDocument.ComputeStatistics(wdStatisticPages, True)
For i = 1 To Pgs
Selection.GoTo What:=wdGoToPage, Which:=wdGoToAbsolute, Name:=Str(i)
' Check whether it has actually gone to page i http://vbaexpress.com/forum/images/smilies/001.gif
If Selection.Information(wdActiveEndPageNumber) <> i Then
j = 1
' Go down 100 lines and see whether you can go to the next page
Do Until j = 100 Or Selection.Information(wdActiveEndPageNumber) = i
Selection.MoveDown Unit:=wdLine, Count:=1
j = j + 1
Loop
'Even after going down 100 lines, if you can't go to the next page, show an warning message.
If j = 100 Then
If MsgBox("There's something wrong with this document that PrintGoodPages macro cannot recognise. If continued unexpected results may occur. Do you want to continue?" _
, vbYesNo, "Abnormal Page Break") = vbNo Then
Exit Sub
End If
End If
End If
sSection = Trim(Str(Selection.Information(wdActiveEndSectionNumber)))
sRelativePage = Trim(Str(Selection.Information(wdActiveEndAdjustedPageNumber)))
' Get the current page in pNsN format.
sCurrentPage = "p" + sRelativePage + "s" + sSection
testAsc = Asc(Selection.Characters.First)
bInclude = False

Do
' Check for the page break
If testAsc = 12 Then
' But, since ascii 12 is also used as section break,
' make sure it's a page break by going one char right.
Selection.MoveRight Unit:=wdCharacter, Count:=1
If Selection.Information(wdActiveEndPageNumber) = i Then
testAsc = Asc(Selection.Characters.First)
Else
Exit Do
End If
End If
' Check for carriage return(13), space(88), etc. (If only these are in a page,
' it's still considered as a blank page.
If testAsc = 13 Or testAsc = 88 Or testAsc = 32 Or testAsc = 21 Then
If Selection.Next(Unit:=wdCharacter, Count:=1) Is Nothing Then
Exit Do
Else
Selection.Next(Unit:=wdCharacter, Count:=1).Select
testAsc = Asc(Selection.Characters.First)
End If
Else
bInclude = True
Exit Do
End If
Loop
If bInclude Then
If bStartOfRange Then
sStartOfRange = sCurrentPage
sEndOfRange = sCurrentPage
bStartOfRange = False
Else
sEndOfRange = sCurrentPage
End If
Else
' Following is the print range generation logic.
If Not bStartOfRange Then
bStartOfRange = True
sTempSectionsNPages = sSectionsNPages
If sSectionsNPages = "" Then
If sStartOfRange = sEndOfRange Then
sSectionsNPages = sStartOfRange
Else
sSectionsNPages = sStartOfRange + "-" + sEndOfRange
End If
Else
If sStartOfRange = sEndOfRange Then
sSectionsNPages = sSectionsNPages + "," + sStartOfRange
Else
sSectionsNPages = sSectionsNPages + "," + sStartOfRange + "-" + sEndOfRange
End If
End If
' Print method can only accept a string less than 256 chars.
' If it's more than 256, printing will be done in steps.
If Len(sSectionsNPages) > 255 Then
sSectionsNPagesArr(k) = sTempSectionsNPages
k = k + 1
If sStartOfRange = sEndOfRange Then
sSectionsNPages = sStartOfRange
Else
sSectionsNPages = sStartOfRange + "-" + sEndOfRange
End If
End If
End If
End If
Next i
' Finalizing print range generation.
If Not bStartOfRange Then
bStartOfRange = True
If sSectionsNPages = "" Then
If sStartOfRange = sEndOfRange Then
sSectionsNPages = sStartOfRange
Else
sSectionsNPages = sStartOfRange + "-" + sEndOfRange
End If
Else
If sStartOfRange = sEndOfRange Then
sSectionsNPages = sSectionsNPages + "," + sStartOfRange
Else
sSectionsNPages = sSectionsNPages + "," + sStartOfRange + "-" + sEndOfRange
End If
End If
End If
sSectionsNPagesArr(k) = sSectionsNPages
For i = 1 To k
If MsgBox("Pages to print are " + sSectionsNPagesArr(i) + ". Do you want to print them?" _
, vbOKCancel, "Confirm Printing Step " + Str(i) + " of " + Str(k)) = vbOK Then
Application.PrintOut FileName:="", Range:=wdPrintRangeOfPages, Item:= _
wdPrintDocumentContent, Copies:=1, Pages:=sSectionsNPagesArr(i), PageType:= _
wdPrintAllPages, ManualDuplexPrint:=False, Collate:=True, Background:= _
True, PrintToFile:=False, PrintZoomColumn:=0, PrintZoomRow:=0, _
PrintZoomPaperWidth:=0, PrintZoomPaperHeight:=0
End If
Next i
ActiveWindow.View.Type = docType
Selection.GoTo What:=wdGoToPage, Which:=wdGoToAbsolute, Count:=currentPageNo
Selection.GoTo What:=wdGoToLine, Which:=wdGoToRelative, Count:=currentLineNo
End Sub