Printing Document

D

DazedAndConfused

I have a 8.5 x 11 landscape document with about 1/4 inch of space on the
left and right where there is no print.
The document displays perfect in print preview, but when I print it, about
1/2 inch on the right is not printed (leaving about 3/4 inch empty margin on
the right side of the page). The left side starts about 1/4 inch in and
prints just like I expect it to.

I tried setting the margins to 0 and OriginAtMargins = True. The print began
about an 1/8" at the left but still stoped about 3/4" on the right.

I know I can change the document so that there is no print for the last 3/4
inch on the right, but that seems like coding around the problem instead of
getting it right.

Is there anything I can do besides coding the width of the report shorter?
 
R

Ron Allen

This sounds like you have hit the 'hard margins' on the printer. Most
printers won't print over a whole page. Some older ink jets have wide
margin areas.
You can PInvoke GetDeviceCaps to find out the actual horizontal/vertical
hard margins on the printer. You could then scale your print area to fit all
your printed items within the actual display area of the printer. If you
search for GetHardMargins you should find some code to get printer margins
that Mr. Wagner converted to VB.
In framework 2.0 you can get this information without calling
GetDeviceCaps and use that to compensate.

Ron Allen
 
D

DazedAndConfused

Thank you for pointing me in the right direction.
Ron Allen said:
This sounds like you have hit the 'hard margins' on the printer. Most
printers won't print over a whole page. Some older ink jets have wide
margin areas.
You can PInvoke GetDeviceCaps to find out the actual
horizontal/vertical hard margins on the printer. You could then scale your
print area to fit all your printed items within the actual display area of
the printer. If you search for GetHardMargins you should find some code
to get printer margins that Mr. Wagner converted to VB.
In framework 2.0 you can get this information without calling
GetDeviceCaps and use that to compensate.

Ron Allen
 
D

DazedAndConfused

Is there a way to scale programmatically? A way to specify that the document
should print 95% of size. Or do you have to code alternate font sizes, x, y,
etc.?

Any good examples you can poit me to?
 
R

Ron Allen

Just call the Graphics.Transform.Scale method on the Graphics being used
and specify the x and y scaling factors and everything will be done for you
as far as shrinking the document.

Ron Allen
 
D

DazedAndConfused

Thank you.

I was able to scale the document using:

e.Graphics.ScaleTransform(0.95F, 0.95F)

In order to scale using Transform.Scale method I had to:

Dim myMatrix As New System.Drawing.Drawing2D.Matrix
myMatrix.Scale(0.95F, 0.95F)
e.Graphics.Transform = myMatrix

Is this the correct way of using Transform.Scale or am I missing the concept
somewhere?

Also I would really like to be able to use Mr. Wagners class for getting the
hard margins but keep getting a System.InvalidOperationException - The
object is currently in use elsewhere and just can't figure it out on my own.

In stepping through the debugger I actually see the
System.InvalidOperationException error first comming up in the Locals window
on the Graphics object at:
Dim hDC As IntPtr = Graphics.GetHdc()

hDC is populated (with the printer handle I assume)

But no ErrorException is thrown until m.Left = CInt(ox * 100 /
Graphics.DpiX); then I get the "The object is currently in use elsewhere"
error.

Please help me to understand??!!!

Here is the code:

Imports System.Drawing.Printing

''' <summary>
''' Provides information about a device.
''' </summary>
Public Class Device
Private Declare Function GetDeviceCaps Lib "gdi32.dll" ( _
ByVal hdc As IntPtr, _
ByVal nIndex As Int32 _
) As Int32

Private Const PHYSICALOFFSETX As Int32 = 112 ' In device units.
Private Const PHYSICALOFFSETY As Int32 = 113 ' In device units.
Private Const HORZRES As Int32 = 8 ' In pixels/dots.
Private Const VERTRES As Int32 = 10 ' In pixels/dots.

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="Graphics">The device's <c>Graphics</c> object.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Public Shared Function GetPhysicalMargins( _
ByVal Graphics As Graphics _
) As Margins
Dim hDC As IntPtr
Try
hDC = Graphics.GetHdc() <========== Locals window shows
Graphics as System.InvalidOperationException, but no error is thrown

Catch ex As InvalidOperationException

End Try
Dim m As Margins = GetPhysicalMargins(hDC, Graphics)
Graphics.ReleaseHdc(hDC)
Return m
End Function

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="hDC">Handle to the device context.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Public Shared Function GetPhysicalMargins( _
ByVal hDC As IntPtr _
) As Margins
Dim g As Graphics = Graphics.FromHdc(hDC)
Dim m As Margins = GetPhysicalMargins(hDC, g)
g.Dispose()
Return m
End Function

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="hDC">Handle to the device context.</param>
''' <param name="Graphics">The device's <c>Graphics</c> object.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Private Shared Function GetPhysicalMargins( _
ByVal hDC As IntPtr, _
ByVal Graphics As Graphics _
) As Margins
Dim m As New Margins
Dim ox As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETX)
Dim oy As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETY)
With m
.Left = CInt(ox * 100 / Graphics.DpiX) <==========
System.InvalidOperationException - The object is currently in use
elsewhere - ERROR IS THROWN HERE
.Top = CInt(oy * 100 / Graphics.DpiY)
.Right = _
CInt( _
(ox + GetDeviceCaps(hDC, HORZRES)) * 100 / _
Graphics.DpiX _
)
.Bottom = _
CInt( _
(oy + GetDeviceCaps(hDC, VERTRES)) * 100 / _
Graphics.DpiY _
)
End With
Return m
End Function
End Class

Public Class Form1
Inherits System.Windows.Forms.Form
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim p As New PrinterSettings
For Each PrinterName As String In PrinterSettings.InstalledPrinters
p.PrinterName = PrinterName
Dim g As Graphics = p.CreateMeasurementGraphics()
MsgBox( _
PrinterName & ControlChars.NewLine & _
Device.GetPhysicalMargins(g).ToString() & "[0.01 inch]" _
)
g.Dispose()
Next PrinterName
End Sub
End Class
 
R

Ron Allen

You need to get the DPIX returned by GetDeviceCaps not by the Graphics.
The HORZRES and VERTRES as defined here are the call values to use to
get the corresponding resolutions as you are doing after your division.
Just get the x and y resolutions first which gives you the # of pixels
across/down of the printer and divide these into the returned size which is
returned in mm. I convert the mm to inches before dividing to get the
actual DPI values
The original I wrote in (C#) is below Herfred did the conversion to VB
quite some time ago.
You don't need to pass the Graphics to anything to compute the hard
margins. All you need is the hDc from the Graphics. Note that I usually
just release the hDc as soon as I get it as it doesn't normally change
during the code I call afterwards.

Also you can just scale using Graphics.Transform.Scale(0.95, 0.95) no
need to setup a matrix.

--------------------original
C# -----------------------------------------------
// the other two constants
public static short HORZSIZE = 4; // Horizontal size in
millimeters
public static short VERTSIZE = 6; // Vertical size in millimeters

/// <summary>
/// Return the device 'hard' margins for the passed in
/// Device Context handle. Return data in 1/100ths inch
/// </summary>
/// <param name="hDc">Input handle</param>
/// <param name="left">output left margin in 1/100ths inch</param>
/// <param name="top">output top margin in 1/100ths inch</param>
/// <param name="right">output right margin in 1/100ths inch</param>
/// <param name="bottom">output bottom margin in 1/100ths inch</param>
public static void GetHardMargins(int hDc, ref float left, ref float top,
ref float right, ref float bottom)
{
float offx = Convert.ToSingle(GetDeviceCaps(hDc, PHYSICALOFFSETX));
float offy = Convert.ToSingle(GetDeviceCaps(hDc, PHYSICALOFFSETY));;
float resx = Convert.ToSingle(GetDeviceCaps(hDc, HORZRES));
float resy = Convert.ToSingle(GetDeviceCaps(hDc, VERTRES));
float hsz = Convert.ToSingle(GetDeviceCaps(hDc, HORZSIZE))/25.4f; //
screen width in inches
float vsz = Convert.ToSingle(GetDeviceCaps(hDc, VERTSIZE))/25.4f; //
screen height in inches
float ppix = resx/hsz;
float ppiy = resy/vsz;
left = (offx/ppix) * 100.0f;
top = (offy/ppix) * 100.0f;
bottom = top + (vsz * 100.0f);
right = left + (hsz * 100.0f);
}

// code that call this
IntPtr hDc = ev.Graphics.GetHdc();
ev.Graphics.ReleaseHdc(hDc);
float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f;
PrintUtils.GetHardMargins(hDc.ToInt32(), ref left, ref top, ref right,
ref bottom);

This works even though I release the device context immediately. Don't
assume that the DPIX/Y returned from the Graphics will be identical to these
as they are rounded (although close enough for most).
--------------------------end C#
code------------------------------------------
DazedAndConfused said:
Thank you.

I was able to scale the document using:

e.Graphics.ScaleTransform(0.95F, 0.95F)

In order to scale using Transform.Scale method I had to:

Dim myMatrix As New System.Drawing.Drawing2D.Matrix
myMatrix.Scale(0.95F, 0.95F)
e.Graphics.Transform = myMatrix

Is this the correct way of using Transform.Scale or am I missing the
concept somewhere?

Also I would really like to be able to use Mr. Wagners class for getting
the hard margins but keep getting a System.InvalidOperationException - The
object is currently in use elsewhere and just can't figure it out on my
own.

In stepping through the debugger I actually see the
System.InvalidOperationException error first comming up in the Locals
window on the Graphics object at:
Dim hDC As IntPtr = Graphics.GetHdc()

hDC is populated (with the printer handle I assume)

But no ErrorException is thrown until m.Left = CInt(ox * 100 /
Graphics.DpiX); then I get the "The object is currently in use elsewhere"
error.

Please help me to understand??!!!

Here is the code:

Imports System.Drawing.Printing

''' <summary>
''' Provides information about a device.
''' </summary>
Public Class Device
Private Declare Function GetDeviceCaps Lib "gdi32.dll" ( _
ByVal hdc As IntPtr, _
ByVal nIndex As Int32 _
) As Int32

Private Const PHYSICALOFFSETX As Int32 = 112 ' In device units.
Private Const PHYSICALOFFSETY As Int32 = 113 ' In device units.
Private Const HORZRES As Int32 = 8 ' In pixels/dots.
Private Const VERTRES As Int32 = 10 ' In pixels/dots.

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="Graphics">The device's <c>Graphics</c> object.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Public Shared Function GetPhysicalMargins( _
ByVal Graphics As Graphics _
) As Margins
Dim hDC As IntPtr
Try
hDC = Graphics.GetHdc() <========== Locals window shows
Graphics as System.InvalidOperationException, but no error is thrown

Catch ex As InvalidOperationException

End Try
Dim m As Margins = GetPhysicalMargins(hDC, Graphics)
Graphics.ReleaseHdc(hDC)
Return m
End Function

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="hDC">Handle to the device context.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Public Shared Function GetPhysicalMargins( _
ByVal hDC As IntPtr _
) As Margins
Dim g As Graphics = Graphics.FromHdc(hDC)
Dim m As Margins = GetPhysicalMargins(hDC, g)
g.Dispose()
Return m
End Function

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="hDC">Handle to the device context.</param>
''' <param name="Graphics">The device's <c>Graphics</c> object.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Private Shared Function GetPhysicalMargins( _
ByVal hDC As IntPtr, _
ByVal Graphics As Graphics _
) As Margins
Dim m As New Margins
Dim ox As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETX)
Dim oy As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETY)
With m
.Left = CInt(ox * 100 / Graphics.DpiX) <==========
System.InvalidOperationException - The object is currently in use
elsewhere - ERROR IS THROWN HERE
.Top = CInt(oy * 100 / Graphics.DpiY)
.Right = _
CInt( _
(ox + GetDeviceCaps(hDC, HORZRES)) * 100 / _
Graphics.DpiX _
)
.Bottom = _
CInt( _
(oy + GetDeviceCaps(hDC, VERTRES)) * 100 / _
Graphics.DpiY _
)
End With
Return m
End Function
End Class

Public Class Form1
Inherits System.Windows.Forms.Form
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim p As New PrinterSettings
For Each PrinterName As String In PrinterSettings.InstalledPrinters
p.PrinterName = PrinterName
Dim g As Graphics = p.CreateMeasurementGraphics()
MsgBox( _
PrinterName & ControlChars.NewLine & _
Device.GetPhysicalMargins(g).ToString() & "[0.01 inch]" _
)
g.Dispose()
Next PrinterName
End Sub
End Class



Ron Allen said:
Just call the Graphics.Transform.Scale method on the Graphics being
used and specify the x and y scaling factors and everything will be done
for you as far as shrinking the document.

Ron Allen
 
D

DazedAndConfused

Thank for your help and especially your patients.

Ron Allen said:
You need to get the DPIX returned by GetDeviceCaps not by the Graphics.
The HORZRES and VERTRES as defined here are the call values to use to
get the corresponding resolutions as you are doing after your division.
Just get the x and y resolutions first which gives you the # of pixels
across/down of the printer and divide these into the returned size which
is returned in mm. I convert the mm to inches before dividing to get the
actual DPI values
The original I wrote in (C#) is below Herfred did the conversion to VB
quite some time ago.
You don't need to pass the Graphics to anything to compute the hard
margins. All you need is the hDc from the Graphics. Note that I usually
just release the hDc as soon as I get it as it doesn't normally change
during the code I call afterwards.

Also you can just scale using Graphics.Transform.Scale(0.95, 0.95) no
need to setup a matrix.

--------------------original
C# -----------------------------------------------
// the other two constants
public static short HORZSIZE = 4; // Horizontal size in
millimeters
public static short VERTSIZE = 6; // Vertical size in
millimeters

/// <summary>
/// Return the device 'hard' margins for the passed in
/// Device Context handle. Return data in 1/100ths inch
/// </summary>
/// <param name="hDc">Input handle</param>
/// <param name="left">output left margin in 1/100ths inch</param>
/// <param name="top">output top margin in 1/100ths inch</param>
/// <param name="right">output right margin in 1/100ths inch</param>
/// <param name="bottom">output bottom margin in 1/100ths inch</param>
public static void GetHardMargins(int hDc, ref float left, ref float top,
ref float right, ref float bottom)
{
float offx = Convert.ToSingle(GetDeviceCaps(hDc, PHYSICALOFFSETX));
float offy = Convert.ToSingle(GetDeviceCaps(hDc, PHYSICALOFFSETY));;
float resx = Convert.ToSingle(GetDeviceCaps(hDc, HORZRES));
float resy = Convert.ToSingle(GetDeviceCaps(hDc, VERTRES));
float hsz = Convert.ToSingle(GetDeviceCaps(hDc, HORZSIZE))/25.4f; //
screen width in inches
float vsz = Convert.ToSingle(GetDeviceCaps(hDc, VERTSIZE))/25.4f; //
screen height in inches
float ppix = resx/hsz;
float ppiy = resy/vsz;
left = (offx/ppix) * 100.0f;
top = (offy/ppix) * 100.0f;
bottom = top + (vsz * 100.0f);
right = left + (hsz * 100.0f);
}

// code that call this
IntPtr hDc = ev.Graphics.GetHdc();
ev.Graphics.ReleaseHdc(hDc);
float left = 0.0f, right = 0.0f, top = 0.0f, bottom = 0.0f;
PrintUtils.GetHardMargins(hDc.ToInt32(), ref left, ref top, ref right,
ref bottom);

This works even though I release the device context immediately. Don't
assume that the DPIX/Y returned from the Graphics will be identical to
these as they are rounded (although close enough for most).
--------------------------end C#
code------------------------------------------
DazedAndConfused said:
Thank you.

I was able to scale the document using:

e.Graphics.ScaleTransform(0.95F, 0.95F)

In order to scale using Transform.Scale method I had to:

Dim myMatrix As New System.Drawing.Drawing2D.Matrix
myMatrix.Scale(0.95F, 0.95F)
e.Graphics.Transform = myMatrix

Is this the correct way of using Transform.Scale or am I missing the
concept somewhere?

Also I would really like to be able to use Mr. Wagners class for getting
the hard margins but keep getting a System.InvalidOperationException -
The object is currently in use elsewhere and just can't figure it out on
my own.

In stepping through the debugger I actually see the
System.InvalidOperationException error first comming up in the Locals
window on the Graphics object at:
Dim hDC As IntPtr = Graphics.GetHdc()

hDC is populated (with the printer handle I assume)

But no ErrorException is thrown until m.Left = CInt(ox * 100 /
Graphics.DpiX); then I get the "The object is currently in use elsewhere"
error.

Please help me to understand??!!!

Here is the code:

Imports System.Drawing.Printing

''' <summary>
''' Provides information about a device.
''' </summary>
Public Class Device
Private Declare Function GetDeviceCaps Lib "gdi32.dll" ( _
ByVal hdc As IntPtr, _
ByVal nIndex As Int32 _
) As Int32

Private Const PHYSICALOFFSETX As Int32 = 112 ' In device units.
Private Const PHYSICALOFFSETY As Int32 = 113 ' In device units.
Private Const HORZRES As Int32 = 8 ' In pixels/dots.
Private Const VERTRES As Int32 = 10 ' In pixels/dots.

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="Graphics">The device's <c>Graphics</c>
object.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Public Shared Function GetPhysicalMargins( _
ByVal Graphics As Graphics _
) As Margins
Dim hDC As IntPtr
Try
hDC = Graphics.GetHdc() <========== Locals window shows
Graphics as System.InvalidOperationException, but no error is thrown

Catch ex As InvalidOperationException

End Try
Dim m As Margins = GetPhysicalMargins(hDC, Graphics)
Graphics.ReleaseHdc(hDC)
Return m
End Function

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="hDC">Handle to the device context.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Public Shared Function GetPhysicalMargins( _
ByVal hDC As IntPtr _
) As Margins
Dim g As Graphics = Graphics.FromHdc(hDC)
Dim m As Margins = GetPhysicalMargins(hDC, g)
g.Dispose()
Return m
End Function

''' <summary>
''' Gets a device's physical margins.
''' </summary>
''' <param name="hDC">Handle to the device context.</param>
''' <param name="Graphics">The device's <c>Graphics</c>
object.</param>
''' <returns>
''' The device's physical margins in 0.001 inch units.
''' </returns>
Private Shared Function GetPhysicalMargins( _
ByVal hDC As IntPtr, _
ByVal Graphics As Graphics _
) As Margins
Dim m As New Margins
Dim ox As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETX)
Dim oy As Int32 = GetDeviceCaps(hDC, PHYSICALOFFSETY)
With m
.Left = CInt(ox * 100 / Graphics.DpiX) <==========
System.InvalidOperationException - The object is currently in use
elsewhere - ERROR IS THROWN HERE
.Top = CInt(oy * 100 / Graphics.DpiY)
.Right = _
CInt( _
(ox + GetDeviceCaps(hDC, HORZRES)) * 100 / _
Graphics.DpiX _
)
.Bottom = _
CInt( _
(oy + GetDeviceCaps(hDC, VERTRES)) * 100 / _
Graphics.DpiY _
)
End With
Return m
End Function
End Class

Public Class Form1
Inherits System.Windows.Forms.Form
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
Dim p As New PrinterSettings
For Each PrinterName As String In PrinterSettings.InstalledPrinters
p.PrinterName = PrinterName
Dim g As Graphics = p.CreateMeasurementGraphics()
MsgBox( _
PrinterName & ControlChars.NewLine & _
Device.GetPhysicalMargins(g).ToString() & "[0.01 inch]" _
)
g.Dispose()
Next PrinterName
End Sub
End Class



Ron Allen said:
Just call the Graphics.Transform.Scale method on the Graphics being
used and specify the x and y scaling factors and everything will be done
for you as far as shrinking the document.

Ron Allen
Is there a way to scale programmatically? A way to specify that the
document should print 95% of size. Or do you have to code alternate
font sizes, x, y, etc.?

Any good examples you can poit me to?
This sounds like you have hit the 'hard margins' on the printer.
Most printers won't print over a whole page. Some older ink jets have
wide margin areas.
You can PInvoke GetDeviceCaps to find out the actual
horizontal/vertical hard margins on the printer. You could then scale
your print area to fit all your printed items within the actual
display area of the printer. If you search for GetHardMargins you
should find some code to get printer margins that Mr. Wagner converted
to VB.
In framework 2.0 you can get this information without calling
GetDeviceCaps and use that to compensate.

Ron Allen
I have a 8.5 x 11 landscape document with about 1/4 inch of space on
the left and right where there is no print.
The document displays perfect in print preview, but when I print it,
about 1/2 inch on the right is not printed (leaving about 3/4 inch
empty margin on the right side of the page). The left side starts
about 1/4 inch in and prints just like I expect it to.

I tried setting the margins to 0 and OriginAtMargins = True. The
print began about an 1/8" at the left but still stoped about 3/4" on
the right.

I know I can change the document so that there is no print for the
last 3/4 inch on the right, but that seems like coding around the
problem instead of getting it right.

Is there anything I can do besides coding the width of the report
shorter?
 

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