Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Printing bitmap using stretchblt - Different printer resolution different picture size.

137 views
Skip to first unread message

Justin

unread,
Apr 20, 2003, 8:44:22 AM4/20/03
to
Hi All,

Appreciate if anybody could help me with my printing problem. I am
trying to print a bitmap (using stretchblt) from a VB picturebox.

I have done this successfully. However I find that if I set the
resolution higher (e.g. 1200 dpi) or lower (180dpi) on a printer, the
picture will shrink or become lower respectively.

Thanks in advance.


Attached below is the VB code.


Justin.

Code
-------
Private Sub printPicture(ByVal printDC As Long, _
ByVal totalPhysicalHeight As Long, _
ByVal bottomMarginLength As Long, _
ByVal totalPhysicalWidth As Long, _
ByVal rightMarginLength As Long)

Const srcCopy = &HCC0020
Const newFrame = 1
Const pixel = 3
Dim hMemoryDc, hOldBitMap, apiError, result As Long
Dim pixelX, pixelY, x, timesEnlarge As Single
Dim positionX, positionY As Long
Dim bottomMarginStartPoint, rightMarginStartPoint, _
pictureStartPointHeight, pictureStartPointWidth As Long

pixelX = 1 / Printer.TwipsPerPixelX
pixelY = 1 / Printer.TwipsPerPixelY
dummyForm.Picture1.Picture = dummyForm.Picture1.Image
dummyForm.Picture1.ScaleMode = pixel

timesEnlarge = 2


bottomMarginStartPoint = (Printer.Height * pixelY) -
bottomMarginLength pictureStartPointHeight = bottomMarginStartPoint
- ((dummyForm.Picture1.ScaleHeight * timesEnlarge) * 1.25)

rightMarginStartPoint = (Printer.Width * pixelX) - rightMarginLength
pictureStartPointWidth = rightMarginStartPoint - ((dummyForm.
Picture1.ScaleWidth * timesEnlarge) * 1.25)

' Display Hour glass
MousePointer = 11


Printer.ScaleMode = 3

hMemoryDc = CreateCompatibleDC(dummyForm.Picture1.hdc)
hOldBitMap = SelectObject(hMemoryDc, dummyForm.Picture1.Picture)

positionX = Printer.ScaleWidth / 2
positionY = pictureStartPointHeight
apiError = StretchBlt(printDC, pictureStartPointWidth,
pictureStartPointHeight, _
dummyForm.Picture1.ScaleWidth * timesEnlarge,
_ dummyForm.Picture1.ScaleHeight *
timesEnlarge, hMemoryDc, 0, 0, _ dummyForm.
Picture1.ScaleWidth, _ dummyForm.Picture1.
ScaleHeight, srcCopy)

hOldBitMap = SelectObject(hMemoryDc, hOldBitMap)
apiError = DeleteDC(hMemoryDc)

MousePointer = 1
End Sub

Mike Williams

unread,
Apr 20, 2003, 12:04:02 PM4/20/03
to
I'm not entirely sure what size you intend your printed picture to be, but
your code as it stands (using a Picture Box that is set to a ScaleMode of
vbPixels as you are doing) will print the picture at a size such that the
number of pixels in the printed output is the same as the number of pixels
in the Picture Box (multiplied by the value you have hard coded into your
timesEnlarge variable):

dummyForm.Picture1.ScaleWidth * timesEnlarge, _
dummyForm.Picture1.ScaleHeight * timesEnlarge, _

The destination nWidth and nHeight variables in the StretchBlt API are in
the logical units of the destination DC, which for a printer are in pixels,
so for any specific picture box size the actual size of the printed output
(measured in inches) will depend on how large each printer pixels actually
is. If (as is the usual case) you want to specify the printed output size in
inches (so that it prints at the same on all printers at all settings) then
you need to take into account the number of pixels per inch the printer is
currently using. For example, if you want a picture that is printed at a
size of 2 inches by 3 inches then you need to do something like:

StretchBlt (Printer.hDC, 0, 0, _
Printer.ScaleX (2, vbInches, vbPixels) _
Printer.ScaleY (3, vbinches, vbPixels) _
hMemoryDC, _
. . . etc
. . . etc

By the way, you should have a look at your Dim statements. VB does *not*
work in the "old fashioned way", where you could specify the Type of a group
of variables "in one go". You need to specify each type individually. For
example:

Dim j, k as Long

will result in j having the Type of Long and k having the default Type of
Variant!

You need to do:

Dim j as Long, k as Long

Mike

"Justin" <ta...@no-spam.hotmail.com> wrote in message
news:Usenet.sbbsmnes@localhost...

Mike Williams

unread,
Apr 20, 2003, 12:08:24 PM4/20/03
to
p.s. I presume that you are using an old version of VB (or perhaps writing
code that will print to DCs other than the standard VB Printer object DC) or
you would have used the PaintPicture method, which will do exactly what you
want in a single line of code. PaintPicture became available from VB5
onwards.

Mike

"Mike Williams" <Mi...@BrandyandCoke.fsnet.co.uk> wrote in message
news:b7uggo$d6a$1...@newsg1.svr.pol.co.uk...

Justin

unread,
Apr 22, 2003, 11:26:56 AM4/22/03
to
Great! Tried Mike's solution and it works like a charm! A few lines of
code and this bug is wipped off the face of the earth.

Also thanks for pointing out my DIM error.

Just a few more addedum to my earlier post:-

a. the parameters I passed in (assuming height is longer part of the A4
paper):-

printDC : Device Context for the printer
totalPhysicalHeight : Height of the Paper (including unprintable)
totalPhysicalWidth : Width of Paper (including unprintable)
bottomMarginLength : The unprintable area of paper's bottom
rightMarginLength : The unprintable area located in the right of the
paper.

b. What I am trying to do.

I am trying to place a picture (fixed size) on bottom right of the
paper. Naturally, I would want the picture to be place starting at
X(totalPhysicalHeight - bottomMarginLength - height of picture),
Y(totalPhysicalWidth - rightMarginLength - width of picture).

Hope it might eliminate any confusions.

Justin

unread,
Apr 22, 2003, 11:36:19 AM4/22/03
to
I am using VB6. In addition to printing the picture, I am also
rotating alot of text onto the printer. The Printer object built in VB
stuffs up for reasons unknown (i.e. it won't rotate my text).

Therefore I had to resort to creating a HDC from scratch, using Logfont
and selectObject.

Windows API isn't for the faint-hearted. I had to do alot of research
before being able to print anything. I found the most helpful resource
is a book called 'Print Programming in Windows' by Jeff Potts [ISBN 0-
87930-585-1]. It puts everything into perspective although he neglected
to mention the Printing pixels problem :)

Mike Williams

unread,
Apr 22, 2003, 2:37:27 PM4/22/03
to
It might be easier for me to reply to both your messages at once. Firstly,
you say that you have resorted to printing to a hDC that you have created
rather than the VB Printer object in order that you can print rotated text,
but you don't have to do that (unless you have other reasons for wanting to
do so). It is perfectly possible to print rotated text using the VB Printer
object. The reason that many coders' attempts to print rotated text to the
Printer object fails is because of a little bug in the printer object that
causes it to "forget" any rotated font you have created for it as soon as
you access one of its properties or use one of its methods. This means that
once you have created your rotated font you cannot use the VB Print method
to print it and you must instead use the API TextOut method. More than that,
you cannot even use the TextOut method in the normal way (passing the
Printer.hDC as one of the parameters) because the very act of VB "looking
at" the printer object in order to find out what its hDC is will cause the
Printer object to "forget" the rotated font that you had previously
painstakingly created! You can very easily get around this problem, though,
by storing the Printer.hDC in a standard Long variable *before* you create
the rotated font and then passing the value of the Long variable. In other
words, instead of doing this:

Dim myfont as LOGFONT
With myfont
' set up the rotated font parameters
End With
TextOut Printer.hdc, x, y, textstring, Len(textstring)

. . . you instead have to do:

Dim myfont as LOGFONT, mydc as Long
mydc = Printer.hdc
With myfont
' set up the rotated font parameters
End With
TextOut mydc, x, y, textstring, Len(textstring)

Using the above method your rotated font will print correctly and, since you
are using the standard VB Printer object, you will be able to use all of the
standard VB Printer object methods (Print, PaintPicture, Line, Circle, etc)
on the same page as your rotated text.

In your other message you mention that you are trying to print a picture at
the bottom right corner of the page, and that you are passing your routine
some parameters that include the sizes of the "unprintable margins". You
haven't shown the code that you are using to get those values, so I can only
presume that you know that you can easily get them using the API
GetDeviceCaps function. If you don't already know this then post again and
I'll tell you more about it. In any case, from your description where you
say "Naturally, I would want the picture to be place starting at


X(totalPhysicalHeight - bottomMarginLength - height of picture), Y

(totalPhysicalWidth - rightMarginLength - width of picture)" it would appear
that you are doing it wrong anyway. You need to be aware that coordinate (0,
0) points to the top left corner of the "printable area" and *not* the top
left corner of the physical page. This applies whether you are using the VB
Printer object methods or the API methods. The "printable area", of course,
is a rectangle that is smaller than the physical page, and which is
positioned on the page at a point which varies from one printer to the next,
but is typically a small fraction of an inch from the top and about 0.13
inches from the left of the physical page (for A4 paper). The height of the
printable area is the ScaleHeigth of the printer and the width is the
ScaleWidth. Since the height and width of the printable area are both
usually about half an inch or more less than the physical height and width
of the page there is an area all around the printable area which is a "no
go" area for your output and it is physically impossible for your VB program
(or for any other application, for that matter) to get the printer to
deposit ink in these "unprintable margins". You can, however, accurately
position stuff on the page by using GetDeviceCaps to "read" the values of
the top and left offsets of the printable area and then adjusting the
Printer's ScaleLeft and ScaleTop properties accordingly. This will cause
location (0, 0) to point to the top left corner of the physical page (rather
than the top left corner of the printable area). Post again if you need help
with that.

In the meantime, you can print your picture at exactly the bottom right
corner of the printable area with very little code. For example, to print a
picture at a size of two inches by two inches so that the bottom right
corner of the picture is exactly at the bottom right corner of the printable
area you just need to do:

Printer.ScaleMode = vbInches
Printer.PaintPicture Picture1.Picture, _
Printer.ScaleWidth - 2, Printer.ScaleHeight - 2, 2, 2

Have fun!

Mike

"Justin" <ta...@no-spam.hotmail.com> wrote in message

news:Usenet.cjridoci@localhost...

0 new messages