Home All Groups Group Topic Archive Search About
Author
22 Nov 2007 5:01 PM
Robert
I am using VB 6.0 SP5.

I am using the following snippet.

'snippet.
    x = Printer.ScaleWidth / 2
    A$ = "(" & Printer.Page & ")"
    Printer.CurrentX = x - Printer.TextWidth(A$) / 2
    Printer.Print A$

Question:
Is there a way to find out the total page count so I can do the following?

x = Printer.ScaleWidth / 2
    A$ = "(" & Printer.Page & ") of (" & ???? & ")"
    Printer.CurrentX = x - Printer.TextWidth(A$) / 2
    Printer.Print A$

--
Thanks in advance
bob
rober***@mountaincable.net

Author
22 Nov 2007 6:03 PM
Mike Williams
"Robert" <rober***@mountaincable.net> wrote in message
news:%23juPUlSLIHA.4688@TK2MSFTNGP06.phx.gbl...

> Is there a way to find out the total page count so I can
> do the following?
> A$ = "(" & Printer.Page & ") of (" & ???? & ")"

There are various different ways of getting a page count without actually
printing the stuff so that you can print your page numbers in that specific
way. It all depends what you are printing. What exactly are you printing,
and how are you currently printing it? You need to be *very* specific.

Mike
Author
23 Nov 2007 3:06 AM
MikeD
Show quote
"Robert" <rober***@mountaincable.net> wrote in message
news:%23juPUlSLIHA.4688@TK2MSFTNGP06.phx.gbl...
>I am using VB 6.0 SP5.
>
> I am using the following snippet.
>
> 'snippet.
>    x = Printer.ScaleWidth / 2
>    A$ = "(" & Printer.Page & ")"
>    Printer.CurrentX = x - Printer.TextWidth(A$) / 2
>    Printer.Print A$
>
> Question:
> Is there a way to find out the total page count so I can do the following?
>
> x = Printer.ScaleWidth / 2
>    A$ = "(" & Printer.Page & ") of (" & ???? & ")"
>    Printer.CurrentX = x - Printer.TextWidth(A$) / 2
>    Printer.Print A$

Using the Printer object, no, not without "pre-printing" because you're
basically printing on-the-fly. So, at any given point, there's no way for
the Printer object to know how much more is going to get printed and thus
provide a total page count.  One relatively easy way is to substitute a
PictureBox control array (each picbox in the array is a page) for the
Printer object.  You need to be sure to setup the picboxes to match the
Printer object's settings...for example Width, Height, ScaleWidth,
ScaleHeight, Font, etc. Instead of using NewPage, load another picbox into
the control array. Then, your total page count is simply the number of
picboxes you have in the control array.  It takes a little bit of work, but
it's not that bad. What I do is have a single routine for printing, and pass
to it a Boolean indicating whether to print or print preview.

--
Mike
Microsoft MVP Visual Basic
Author
23 Nov 2007 5:55 AM
Stefan Berglund
On Thu, 22 Nov 2007 22:06:31 -0500, "MikeD" <nob***@nowhere.edu> wrote:
in <ejHzZ3XLIHA.3***@TK2MSFTNGP02.phx.gbl>

Show quote
>
>"Robert" <rober***@mountaincable.net> wrote in message
>news:%23juPUlSLIHA.4688@TK2MSFTNGP06.phx.gbl...
>>I am using VB 6.0 SP5.
>>
>> I am using the following snippet.
>>
>> 'snippet.
>>    x = Printer.ScaleWidth / 2
>>    A$ = "(" & Printer.Page & ")"
>>    Printer.CurrentX = x - Printer.TextWidth(A$) / 2
>>    Printer.Print A$
>>
>> Question:
>> Is there a way to find out the total page count so I can do the following?
>>
>> x = Printer.ScaleWidth / 2
>>    A$ = "(" & Printer.Page & ") of (" & ???? & ")"
>>    Printer.CurrentX = x - Printer.TextWidth(A$) / 2
>>    Printer.Print A$
>
>Using the Printer object, no, not without "pre-printing" because you're
>basically printing on-the-fly. So, at any given point, there's no way for
>the Printer object to know how much more is going to get printed and thus
>provide a total page count.  One relatively easy way is to substitute a
>PictureBox control array (each picbox in the array is a page) for the
>Printer object.  You need to be sure to setup the picboxes to match the
>Printer object's settings...for example Width, Height, ScaleWidth,
>ScaleHeight, Font, etc. Instead of using NewPage, load another picbox into
>the control array. Then, your total page count is simply the number of
>picboxes you have in the control array.  It takes a little bit of work, but
>it's not that bad. What I do is have a single routine for printing, and pass
>to it a Boolean indicating whether to print or print preview.

Doesn't that use an awful lot of memory?

---
Stefan Berglund
Author
23 Nov 2007 2:19 PM
MikeD
"Stefan Berglund" <sorry.no.kool***@for.me> wrote in message
news:poqck3hd1vuisudp9gq2hi9demn1oidm2o@4ax.com...

>
> Doesn't that use an awful lot of memory?
>

Only temporarily....and it's not an extraordinary amount.  Of course, it
depends on how much you're printing.

--
Mike
Microsoft MVP Visual Basic
Author
23 Nov 2007 8:02 AM
Mike Williams
"MikeD" <nob***@nowhere.edu> wrote in message
news:ejHzZ3XLIHA.3916@TK2MSFTNGP02.phx.gbl...

> Using the Printer object, no, not without "pre-printing" because
> you're basically printing on-the-fly. So, at any given point, there's
> no way for the Printer object to know how much more is going
> to get printed and thus provide a total page count. One relatively
> easy way is to substitute a PictureBox control array (each picbox
> in the array is a page) for the Printer object.  You need to be sure
> to setup the picboxes to match the Printer object's settings...for
> example Width, Height, ScaleWidth, ScaleHeight, Font, etc.
> Instead of using NewPage, load another picbox into . . .

Actually that approach sometimes doesn't work very well and is unreliable,
and in some cases you can end up printing "1 of 3", "2 of 3", "3 of 3", "4
of 3" on some documents. There are various reasons for this, but the main
one is the fact that you often do not get exactly the same font size and
text height (or text width) on the printer as you do on the picture box (or
other screen device) because of the widely different dpi resolutions and
because of the fact that both the width and the height of characters are
constrained to whole device pixel values. Therefore, lines of text are
likely to wrap at different "end of line" words and also the height of the
lines of text themselves are likely to be different on the printer than they
are on the picture box. To do the job accurately you really need to perform
all calculations and base all your "next printed line" and "next page"
decisions on the values returned by the actual target device (the printer).
There are various different ways of doing this, depending on exactly what
the OP is printing, which is why I asked him for further details. One very
simple way (which incidentally I would not recommend because it might be
troublesome on some printers) is to print your document to the printer on
the "dummy run that counts the pages" in the normal way but to end the print
job with Printer.KillDoc instead of Printer.EndDoc, and then follow that
with a normal print run. However, I really wouldn't recommend doing it that
way. There are of course various other methods that work very well, but the
choice of method to use will depend a lot on what the OP is printing and how
he is currently wrapping his blocks of text (or whatever he is printing).
Something somewhere in his code is deciding which word to wrap each line on
and which line to start a new page on. All the OP needs to do is tap into
that on the "dummy run" by adding up the various widths and heights on the
target device (the printer) without actually printing them. By the way, when
performing such a task in any stuff you are wrapping yourself in standard VB
code you should always use GetTextExtentPoint32 rather than the VB
TextHeight and TextWidth functions because TextHeight and TextWidth are not
usually totally accurate on devices which do not have a whole number of
twips per pixel. TextHeight and TextWidth are usually okay on the screen and
on most Epson printers, but they often return slightly inaccurate values on
HP printers and various others, whereas GetTextExtentPoint32 is always
accurate. Anyway, I'm assuming that the OP will eventually post back with
further details.

Mike
Author
23 Nov 2007 2:23 PM
MikeD
Show quote
"Mike Williams" <mi***@whiskyandCoke.com> wrote in message
news:%23seQ2caLIHA.5116@TK2MSFTNGP03.phx.gbl...
> "MikeD" <nob***@nowhere.edu> wrote in message
> news:ejHzZ3XLIHA.3916@TK2MSFTNGP02.phx.gbl...
>
>> Using the Printer object, no, not without "pre-printing" because
>> you're basically printing on-the-fly. So, at any given point, there's
>> no way for the Printer object to know how much more is going
>> to get printed and thus provide a total page count. One relatively
>> easy way is to substitute a PictureBox control array (each picbox
>> in the array is a page) for the Printer object.  You need to be sure
>> to setup the picboxes to match the Printer object's settings...for
>> example Width, Height, ScaleWidth, ScaleHeight, Font, etc.
>> Instead of using NewPage, load another picbox into . . .
>
> Actually that approach sometimes doesn't work very well and is unreliable,
> and in some cases you can end up printing "1 of 3", "2 of 3", "3 of 3", "4
> of 3" on some documents.

All I can say is that I've used it for years and never had any major issues.

--
Mike
Microsoft MVP Visual Basic
Author
23 Nov 2007 3:38 PM
Mike Williams
"MikeD" <nob***@nowhere.edu> wrote in message
news:unZd4xdLIHA.5468@TK2MSFTNGP05.phx.gbl...

> All I can say is that I've used it for years and never
> had any major issues.

Yes. In many cases such a technique actually will come up with the correct
number of pages, but there are times when it will not, because of the
sometimes different word wrapping points which can result in a different
number of lines being printed on one device than on the other and also
because of sometimes different height of each line of text. If the last line
of text on the last "page" in the Picture Box happens to end up somewhere in
the region from 5 percent to 95 per cent or so of the overall printing
height then it is likely (but not certain) that the number of "pages" in the
Picture Box will be the same as the number of pages on the printer, but
otherwise (if it is in either the top or the bottom 5 per cent of the
overall printing height) there is a fair chance that the number of pages
will be different. So in most cases, as you have discovered, you can get
away with it, but sooner or later you will end up printing and possibly
sending out to a customer a document that has "1 of 7" etc for the page
numbers but that actually has 6 pages, or perhaps 8.

Mike
Author
24 Nov 2007 2:35 AM
MikeD
Show quote
"Mike Williams" <mi***@whiskyandCoke.com> wrote in message
news:%23Tzm8beLIHA.5360@TK2MSFTNGP03.phx.gbl...
> "MikeD" <nob***@nowhere.edu> wrote in message
> news:unZd4xdLIHA.5468@TK2MSFTNGP05.phx.gbl...
>
>> All I can say is that I've used it for years and never
>> had any major issues.
>
> Yes. In many cases such a technique actually will come up with the correct
> number of pages, but there are times when it will not, because of the
> sometimes different word wrapping points which can result in a different
> number of lines being printed on one device than on the other and also
> because of sometimes different height of each line of text. If the last
> line of text on the last "page" in the Picture Box happens to end up
> somewhere in the region from 5 percent to 95 per cent or so of the overall
> printing height then it is likely (but not certain) that the number of
> "pages" in the Picture Box will be the same as the number of pages on the
> printer, but otherwise (if it is in either the top or the bottom 5 per
> cent of the overall printing height) there is a fair chance that the
> number of pages will be different. So in most cases, as you have
> discovered, you can get away with it, but sooner or later you will end up
> printing and possibly sending out to a customer a document that has "1 of
> 7" etc for the page numbers but that actually has 6 pages, or perhaps 8.


Never happened to me yet...or at least never reported to me yet.  I'll stick
with what I got for now and cross that bridge when and if I ever have to.
But, what you say does indeed make sense.

--
Mike
Microsoft MVP Visual Basic
Author
24 Nov 2007 2:18 PM
Mike Williams
"MikeD" <nob***@nowhere.edu> wrote in message
news:%232d1dKkLIHA.5328@TK2MSFTNGP05.phx.gbl...

> Never happened to me yet [incorrect page number
> calculation] ...or at least never reported to me yet.
> I'll stick with what I got for now and cross that bridge
> when and if I ever have to.
> But, what you say does indeed make sense.

Pre-counting the pages using the method I suggested (using GetTextExtent and
DrawText and stuff and simply adding up the coordinates on a dummy run
without actually printing it) returns an accurate result and works well and
does not require a great deal of extra code, but I must admit that I don't
often use that approach myself any more.

If I want to print a document in such a way that I can do things like "Page
1 of 7" etc and if the document is suitable for insertion in a RichTextBox
(mostly text and not much graphics) then I draw the whole document into a
RichTextBox and use the various RTB methods and messages to perform the task
using my own modification of some RTB WYSYWIG code that I came across on
MSDN somewhere. The WSYWIG part is a bit "messy" though and not very
flexible for most purposes, and so these days I only use the RTB method when
I want "Page 1 of 7" and headers and footers etc in a multipage document and
I don't actually want any print preview. When printpreview is required I
prefer to create the individual pages as metafiles (emf). This method allows
you to easily print the pages when required by simply sending the "page
metafiles" to the printer and it allows very flexible print preview because
you can send the same metafiles to a Picture Box or whatever and they will
draw as an exact copy of the page at any desired size you wish (full page,
actual size with scrolling, reduced size, zoom and pan, or whatever you
wish) and the lines of text on the display will wrap at exactly the same
words as they do on the printer with each page having exactly the same
number of lines as it does on the printer. I've only got the "bare bones" of
that method going at the moment (although it works fine as far as I've taken
it) but if you are interested in the RichTextBox method then here's an
example. There are two blocks of code, one for a module and the other for a
Form containing a RichTextBox and a Command Button. Run the program and
paste a few pages worth of stuff (from a MS Word document or whatever) into
the RTB and then click the button:

Mike

' *** START OF FORM CODE ***
Option Explicit
' (By Mike Williams) Code to print a multipage document
' from a RichTextBox with user selectable all round
' page margins and with page numbers and headers/footers
Private Sub PrintRTB(RTB As RichTextBox, _
  marginsize As Single, footer As String)
Dim charsToPrint As Long, nextChar As Long
Dim pageWide As Single, pageHigh As Single
Dim margin As Single, printControl As Boolean
Dim nPage As Long, totalPages As Long
Dim header As String
charsToPrint = Len(RichTextBox1.Text)
If charsToPrint < 1 Then Exit Sub
Printer.Orientation = vbPRORPortrait
Printer.Print ' always start a print job with this
With Printer
  .ScaleMode = vbInches
  .Font.Name = "Times new Roman" ' font for header/footer
  .Font.Size = 10
  pageWide = .ScaleX(.Width, vbTwips, vbInches)
  pageHigh = .ScaleY(.Height, vbTwips, vbInches)
End With
margin = marginsize
SetPrinterOrigin 0, 0 ' only required for VB methods
nextChar = 0: totalPages = 0
printControl = False 'Don't print, just count the pages
Do
  nextChar = PrintRTF(RichTextBox1, margin, margin, _
               pageWide - margin * 2, pageHigh - margin _
               * 2, nextChar, printControl)
  totalPages = totalPages + 1
Loop Until (nextChar > charsToPrint) Or nextChar = 0
nextChar = 0: nPage = 0
printControl = True ' actually print the stuff
Do
  With Printer
  .Font.Italic = True
  .CurrentY = pageHigh - 0.5 - Printer.TextHeight(footer)
  ' I've had occasional problems with .TextWidth in
  ' a With Printer block in Vista, which is why I've
  ' used Printer.TextWidth below
  .CurrentX = pageWide / 2 - Printer.TextWidth(footer) / 2
  Printer.Print footer;
  nPage = nPage + 1
  header = "Page " & Format(nPage) & _
                   " of " & Format(totalPages)
  .Font.Italic = False
  .CurrentY = 0.5 'pageHigh - 0.6
  .CurrentX = (pageWide - Printer.TextWidth(footer)) / 2
  Printer.Print header;
  nextChar = PrintRTF(RichTextBox1, margin, margin, _
               pageWide - margin * 2, pageHigh - _
               margin * 2, nextChar, printControl)
  If nextChar <= charsToPrint Then
    Printer.NewPage
  End If
  End With
Loop Until (nextChar > charsToPrint) Or nextChar = 0
Printer.EndDoc
End Sub

Private Sub Command1_Click()
' Print RTB with a one inch all round margin for the
' body (the header and footer positions are hard
' coded into the function at the moment, but that can
' of course be changed later).
PrintRTB RichTextBox1, 1, "Drink rum responsibly."
End Sub
' *** END OF FORM CODE ***
'
'
'
' *** START OF MODULE CODE ***
Option Explicit
Private Declare Function GetDeviceCaps Lib "gdi32" _
  (ByVal hdc As Long, ByVal nIndex As Long) As Long
Public Declare Function SendMessage Lib "user32" _
  Alias "SendMessageA" (ByVal hwnd As Long, _
  ByVal msg As Long, ByVal wp As Long, lp As Any) As Long
Private Type Rect
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type
Private Type CharRange
  cpMin As Long
  cpMax As Long
End Type
Private Type FormatRange
  hdc As Long
  hdcTarget As Long
  rc As Rect
  rcPage As Rect
  chrg As CharRange
End Type
Private Const WM_USER As Long = &H400
Private Const EM_FORMATRANGE As Long = WM_USER + 57
Private Const EM_SETTARGETDEVICE As Long = WM_USER + 72
Private Const PHYSICALOFFSETX As Long = 112
Private Const PHYSICALOFFSETY As Long = 113

Public Sub SetPrinterOrigin(x As Single, y As Single)
With Printer
  .ScaleLeft = .ScaleX(GetDeviceCaps(.hdc, PHYSICALOFFSETX), _
    vbPixels, .ScaleMode) - x
  .ScaleTop = .ScaleY(GetDeviceCaps(.hdc, PHYSICALOFFSETY), _
    vbPixels, .ScaleMode) - y
  .CurrentX = 0
  .CurrentY = 0
End With
End Sub

Public Function PrintRTF(RTF As RichTextBox, x1 As Single, _
  y1 As Single, Wide As Single, High As Single, _
  charPos As Long, printControl As Boolean) As Long
Dim LeftOffset As Long, TopOffset As Long
Dim LeftMargin As Long, TopMargin As Long
Dim RightMargin As Long, BottomMargin As Long
Dim fr As FormatRange, rcDrawTo As Rect
Dim rcPage As Rect
Dim NextCharPosition As Long, r As Long
LeftOffset = Printer.ScaleX(GetDeviceCaps(Printer.hdc, _
         PHYSICALOFFSETX), vbPixels, vbTwips)
TopOffset = Printer.ScaleY(GetDeviceCaps(Printer.hdc, _
         PHYSICALOFFSETY), vbPixels, vbTwips)
LeftMargin = Printer.ScaleX(x1, Printer.ScaleMode, _
         vbTwips) - LeftOffset
TopMargin = Printer.ScaleY(y1, Printer.ScaleMode, _
         vbTwips) - TopOffset
RightMargin = LeftMargin + Printer.ScaleX(Wide, _
        Printer.ScaleMode, vbTwips)
BottomMargin = TopMargin + Printer.ScaleY(High, _
        Printer.ScaleMode, vbTwips)
rcPage.Left = 0
rcPage.Top = 0
rcPage.Right = Printer.ScaleX(Printer.ScaleWidth, _
        Printer.ScaleMode, vbTwips)
rcPage.Bottom = Printer.ScaleY(Printer.ScaleHeight, _
        Printer.ScaleMode, vbTwips)
rcDrawTo.Left = LeftMargin
rcDrawTo.Top = TopMargin
rcDrawTo.Right = RightMargin
rcDrawTo.Bottom = BottomMargin
fr.hdc = Printer.hdc
fr.hdcTarget = Printer.hdc
fr.rc = rcDrawTo
fr.rcPage = rcPage
fr.chrg.cpMin = charPos
fr.chrg.cpMax = -1
  PrintRTF = True
  PrintRTF = SendMessage(RTF.hwnd, _
    EM_FORMATRANGE, printControl, fr)
r = SendMessage(RTF.hwnd, EM_FORMATRANGE, False, ByVal _
  CLng(0))
End Function
' *** END OF MODULE CODE ***
Author
24 Nov 2007 3:27 PM
MikeD
"Mike Williams" <mi***@whiskyandCoke.com> wrote in message
news:OnBmrTqLIHA.5360@TK2MSFTNGP03.phx.gbl...
> it) but if you are interested in the RichTextBox method then here's an
> example. There are two blocks of code, one for a module and the other for
> a Form containing a RichTextBox and a Command Button. Run the program and
> paste a few pages worth of stuff (from a MS Word document or whatever)
> into the RTB and then click the button:
>


Thanks Mike.  Stashed away for future reference.

--
Mike
Microsoft MVP Visual Basic
Author
24 Nov 2007 4:22 PM
Mike Williams
"MikeD" <nob***@nowhere.edu> wrote in message
news:%23NDhG6qLIHA.1184@TK2MSFTNGP04.phx.gbl...

> Thanks Mike.  Stashed away for future reference.

You're welcome. By the way, I've just noticed that I've got the horizontal
alignment of the header (the page numbers) wrong because I've inadvertently
used the width of the footer instead of the width header in the calculation.
Here is the code modified to correct that fault (I'm posting only the second
Do Loop block, modified so as to correct the problem, to save posting a lot
of code again):

Do
  With Printer
  .Font.Italic = True
  .CurrentY = pageHigh - 0.5 - Printer.TextHeight(footer)
  ' I've had occasional problems with .TextWidth in
  ' a With Printer block in Vista, which is why I've
  ' used Printer.TextWidth below
  .CurrentX = pageWide / 2 - Printer.TextWidth(footer) / 2
  Printer.Print footer;
  nPage = nPage + 1
  header = "Page " & Format(nPage) & _
                   " of " & Format(totalPages)
  .Font.Italic = False
  .CurrentY = 0.5
  .CurrentX = (pageWide - Printer.TextWidth(header)) / 2
  Printer.Print header;
  nextChar = PrintRTF(RichTextBox1, margin, margin, _
               pageWide - margin * 2, pageHigh - _
               margin * 2, nextChar, printControl)
  If nextChar <= charsToPrint Then
    Printer.NewPage
  End If
  End With
Loop Until (nextChar > charsToPrint) Or nextChar = 0

Mike

AddThis Social Bookmark Button