Printing images utilizing entire paper area.

F

Frank Rizzo

I am trying to print huge images (much bigger than target paper). I try
and use e.PageSettings.HardMarginX and e.PageSettings.HardMarginY in the
PrintDocument's PrintPage event to try and determine the maximum area
that I can print on. However, the edge of the image invariably gets cut
off, as if the HardMargin info is wrong. I posted the code below as I
can't understand what I am doing wrong. Is information in
e.PageSettings reliable?


public void Print()
{
if (PicBox.Image.Height <= PicBox.Image.Width)
printImage.DefaultPageSettings.Landscape = true;
else
printImage.DefaultPageSettings.Landscape = false;

printImage.Print();
}

private void printImage_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
// depending on the size of the image,
// we might have to constrain
// the width and height or neither

// if we constrain width, we must proportionately
// constrain the height as well and vice versa

//Rectangle printRectangle = e.PageBounds;
float widthConstraintRatio = 0;
float heightConstraintRatio = 0;
float commonConstraintRatio = 0;

// increase the hard margin just for safety.
float hardMarginX = e.PageSettings.HardMarginX;
float hardMarginY = e.PageSettings.HardMarginY;

RectangleF printRectangle = new RectangleF(
hardMarginX,
hardMarginY,
e.PageBounds.Width - (hardMarginX * 2),
e.PageBounds.Height - (hardMarginY * 2));


if (printRectangle.Width < PicBox.Width)
widthConstraintRatio =
(float)printRectangle.Width / (float)PicBox.Width;

if (printRectangle.Height < PicBox.Height)
heightConstraintRatio =
(float)printRectangle.Height / (float)PicBox.Height;


if (widthConstraintRatio > 0 && heightConstraintRatio > 0)
{
if (widthConstraintRatio >= heightConstraintRatio)
commonConstraintRatio = heightConstraintRatio;
else
commonConstraintRatio = widthConstraintRatio;
}
else if (widthConstraintRatio > 0 &&
heightConstraintRatio == 0)
commonConstraintRatio = widthConstraintRatio;

else if (widthConstraintRatio == 0 &&
heightConstraintRatio > 0)
commonConstraintRatio = heightConstraintRatio;

if (commonConstraintRatio > 0)
{
printRectangle.Height =
PicBox.Height * commonConstraintRatio;
printRectangle.Width =
PicBox.Width * commonConstraintRatio;
}

e.Graphics.DrawImage(PicBox.Image, printRectangle);
e.HasMorePages = false;
}
 
P

Peter Duniho

I am trying to print huge images (much bigger than target paper). I try
and use e.PageSettings.HardMarginX and e.PageSettings.HardMarginY in the
PrintDocument's PrintPage event to try and determine the maximum area
that I can print on. However, the edge of the image invariably gets cut
off, as if the HardMargin info is wrong. I posted the code below as I
can't understand what I am doing wrong. Is information in
e.PageSettings reliable?

I have to admit, I spent several years of my programming career
maintaining the printing code for a well-known productivity application,
and somehow managed to get through all that without ever hearing the term
"hard margin". Unfortunately, the MSDN documentation doesn't at all help
to explain what the HardMarginX and HardMarginY properties represent.

What I do know is that you can't completely describe the area on the page
that the printer can't print to with just two numbers. Many ink jet
printers in particular have asymmetrical physical margins (which is whatI
suppose they are now calling "hard margins"), and you need a complete
rectangle to describe that. I think it's possible that you're running
into an issue where the "hard margins" properties are describing correctly
one side of the page, but not the other.

Fortunately, in PageSettings there is a PrintableArea property that ought
to be what I'm familiar with as the actual area on the page that the
printer can physically print to. It *seems* like you ought to be able to
use that to accomplish what you want.

Of course, I'll also note that there is a MarginBounds property for the
PrintPageEventArgs that represents the margins set by the user. It seems
to me that's a more appropriate boundary to use to restrict your output
anyway. :) The printable area is more commonly used to restrict the
user's choice of margins, rather than being used as margins themselves.

Finally, if I may, I'd like to offer a simpler way to calculate your final
"commonConstraintRatio" value:

float widthConstraintRatio, heightConstraintRatio,
commonConstraintRatio;

widthConstraintRatio = (float)printRectangle.Width / PixBox.Width;
heightConstraintRatio = (float)printRectangle.Height / PixBox.Height;

commonConstraintRatio = Math.Min(1.0f, Math.Min(widthConstraintRatio,
heightConstaintRatio));
printRectangle = new RectangleF(printRectangle.Location, new
SizeF(PixBox.Width * commonConstraintRatio, PixBox.Height *
commonConstraintRatio));

I'll also point out that in the code you posted, if the image you're
printing actually fits entirely within the printRectangle you calculate to
start, you never wind up incorporating the PixBox dimensions into the size
of the printRectangle, which would mess up the output aspect ratio of your
printed image.

Pete
 
L

Linda Liu [MSFT]

Hi Frank,

The hard margin specified by the
PrintPageEventArgs.PageSettings.HardMarginX or HardMarginY represents the
physical margin set by the printer.

When we need to get the area on a page that can be printed on, we usually
use PrintPageEventArgs.MarginBounds property, which get the rectangle area
that represents the portion of the page inside the margins.

Hope this helps.

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
F

Frank Rizzo

As far as I understand, HardMargins represent the max area that the
printer is capable of. Also, thanks for the simplified code. I was
unaware of the Math class.

However, the if I use PrintableArea is basicaly a wrapper around
HardMargins (e.g. displays the same values), so the image still cuts off.

I can't use MarginBounds because it wastes too much of the paper and the
users can't specify margins anyway (it's a kiosk application).

I basically added 25 pixels to the hard margins and that seems to work
ok every time, even though it seems like a hack.

float hardMarginX = e.PageSettings.HardMarginX + 25;
float hardMarginY = e.PageSettings.HardMarginY + 25;

RectangleF printRectangle = new RectangleF(
hardMarginX,
hardMarginY,
e.PageBounds.Width - (hardMarginX * 2),
e.PageBounds.Height - (hardMarginY * 2));

Regards
 
P

Peter Duniho

As far as I understand, HardMargins represent the max area that the
printer is capable of. Also, thanks for the simplified code. I was
unaware of the Math class.

However, the if I use PrintableArea is basicaly a wrapper around
HardMargins (e.g. displays the same values), so the image still cuts off.

I assure you that at least for many printers (ink jet printers in
particular), PrintableArea definitely does not just "wrap" the HardMarginX
and HardMarginY properties. As I said, the printable area can be
asymmetrical, and so two values cannot completely describe the printable
area.

That said, if you are seeing values in PrintableArea that *are* exactly
the same as obtained through the HardMarginX/Y properties, then at least
for that printer there is obviously a correlation and I would agree that
you should be able to use them as a reliable indication of where the
printer can actually print to.
I can't use MarginBounds because it wastes too much of the paper and the
users can't specify margins anyway (it's a kiosk application).

Well, you could specify the margins in code. Doing so would make your
code more reusable (in that if a UI did get added where the user could
specify margins, it would just work). But I suppose if you want to be
tied to the printer hardware, that's certainly a design choice you can
make.
I basically added 25 pixels to the hard margins and that seems to work
ok every time, even though it seems like a hack.

It is a hack. You might consider looking at other possibilities (I wish I
had more direct experience with the .NET printing stuff...a lot of this I
should know off the top of my head, but simply don't). For example, make
sure that your output is not being automatically clipped to the
MarginBounds rectangle to start with. Also, make sure that the origin for
drawing is where you expect it to be. These are things that could make it
appear as though the HardMarginX/Y properties are unreliable, when in fact
they are just being misinterpreted or don't tell the whole story.

Pete
 
F

Frank Rizzo

Peter said:
It is a hack. You might consider looking at other possibilities (I wish
I had more direct experience with the .NET printing stuff...a lot of
this I should know off the top of my head, but simply don't). For
example, make sure that your output is not being automatically clipped
to the MarginBounds rectangle to start with. Also, make sure that the
origin for drawing is where you expect it to be. These are things that
could make it appear as though the HardMarginX/Y properties are
unreliable, when in fact they are just being misinterpreted or don't
tell the whole story.

Pete, you are absolutely right - I just noticed it today. The output is
being clipped at top and left, but not bottom and right. E.g. the
output starts at about the coordinates, specified by MarginBounds
property. How do I work around that? I am just doing

e.Graphics.DrawImage(PicBox.Image, printRectangle);

Is there something I can do to make sure that the output is not clipped.
 
P

Peter Duniho

Pete, you are absolutely right - I just noticed it today. The output is
being clipped at top and left, but not bottom and right. E.g. the
output starts at about the coordinates, specified by MarginBounds
property. How do I work around that? I am just doing

e.Graphics.DrawImage(PicBox.Image, printRectangle);

Is there something I can do to make sure that the output is not clipped.

I wish I had time to play with this printing stuff right now, but
unfortunately I don't. So any advice I offer is going to be vague. I
apologize in advance for that.

But, based on what you've written so far, it sure seems likely to be that
the Graphics getting passed to you in the PrintPage event handler has been
preset to clip to the MarginBounds set in the print job.

I will reiterate my suggestion that it is better to use the PrintableArea
property to determine where the physical limits of the printer are. I see
two possibilities when the printer has asymmetrical physical margings:
either the "HardMarginX/Y" properties are conservative and take the larger
of the two edge margins in each direction, or they are not and take the
smaller. In the former case, you will not actually be using the full
available area for printing, and in the latter case your output may still
wind up getting clipped. The PrintableArea property gives you a complete
description of where the printer is actually able to print, and IMHO is
what should be used.

Frankly, it's not clear to me why Microsoft even included the
"HardMarginX/Y" properties, given this limitation. But then, I have a bit
of history with them misinterpreting suggestions from application
developers and doing strange things with the printing API based on those
suggestions. So my guess is that they meant well, even if the end results
was odd. :)

Anyway, that said, to correct the issue you're seeing, I see a couple of
possibilities. One is to mess with the clipping. You can remove the
clipping before drawing, by setting the Graphics.Clip property to a
default Region instance ("new Region()"). Alternatively, you could use
the PrintableArea property as the parameter to the Graphics.SetClip()
method, so that the clipping region equals the printable area.

IMHO, the second and somewhat better solution would be to change the code
so that rather than looking at the printable area to decide where to draw,
use the margins. Then just make sure that you explicitly set the margins
so that they match the printable area before you start printing. I feel
this is better for reasons that I brought up before: the margins are
really what are normally used for determining where to print, and if and
when the code allows for the user to adjust the margins themselves, then
nothing else needs to be changed. This also avoids having to worry about
any behind-the-scenes clipping that the printer driver might be doing
based on the margins and which you can't override using the Clip property
of the printing Graphics instance you get in the PrintPage handler.

Hope that helps.

Pete
 
F

Frank Rizzo

Peter Duniho wrote: On Tue, 29 May 2007 21:31:28 -0700, Frank Rizzo &lt;[email protected]&gt; wrote:

Pete, you are absolutely right - I just noticed it today.  The output is being clipped at top and left, but not bottom and right.  E.g. the output starts at about the coordinates, specified by MarginBounds property. How do I work around that?  I am just doing

    e.Graphics.DrawImage(PicBox.Image, printRectangle);

Is there something I can do to make sure that the output is not clipped. First off, sorry for non-text mode of this post - the code looks like crap that way.
I figured the problem out with the help of an individual from MS Support.  Basically forget about all the values in e.PageBounds or e.PageSettings.PrintableArea.  You have to drop down to the WinAPI to get what you need.  First off, the top left your print are will start at  0,0 , not the HardMargins.  And height and width can be gotten via GetDeviceCaps.  Here is the entire code below, including a class to retrieve the data from WinAPI:

private void printImage_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
    // depending on the size of the image, we might have to constrain 
    // the width and height or neither
 
    // if we constrain width, we must proportionately
    // constrain the height as well and vice versa

    float widthConstraintRatio = 0;
    float heightConstraintRatio = 0;
    float commonConstraintRatio = 0;
 
    PrinterBounds pb = new PrinterBounds(e);
    RectangleF printRectangle = new RectangleF(0, 0, pb.Width, pb.Height);
 
    if (printRectangle.Width &lt; PicBox.Width)
        widthConstraintRatio = (float)printRectangle.Width / (float)PicBox.Width;
   
    if (printRectangle.Height &lt; PicBox.Height)
        heightConstraintRatio = (float)printRectangle.Height / (float)PicBox.Height;
 
 
    if (widthConstraintRatio &gt; 0 &amp;&amp; heightConstraintRatio &gt; 0)
    {
        if (widthConstraintRatio &gt;= heightConstraintRatio)
            commonConstraintRatio = heightConstraintRatio;
        else
            commonConstraintRatio = widthConstraintRatio;
    }
    else if (widthConstraintRatio &gt; 0 &amp;&amp; heightConstraintRatio == 0)
        commonConstraintRatio = widthConstraintRatio;
    else if (widthConstraintRatio == 0 &amp;&amp; heightConstraintRatio &gt; 0)
        commonConstraintRatio = heightConstraintRatio;
 
    if (commonConstraintRatio &gt; 0)
    {
        printRectangle.Height = PicBox.Height * commonConstraintRatio;
        printRectangle.Width = PicBox.Width * commonConstraintRatio;
    }
 
    e.Graphics.DrawImage(PicBox.Image, printRectangle);
    e.HasMorePages = false;           
}
 
private class PrinterBounds
{
    #region Legacy API
    [DllImport("gdi32.dll")]
    private static extern Int32 GetDeviceCaps(IntPtr hdc, Int32 capindex);
    #endregion
 
    #region Constants
    private const int PHYSICALOFFSETX = 112;
    private const int PHYSICALOFFSETY = 113;
    private const int HORZRES = 8;
    private const int VERTRES = 10;
    #endregion
 
    #region Public Variables
    public readonly int HardMarginLeft;
    public readonly int HardMarginTop;
    public readonly int Width;
    public readonly int Height; 
    #endregion
 
    #region Constructor
    public PrinterBounds(PrintPageEventArgs e)
    {
        IntPtr hDC = e.Graphics.GetHdc();
 
        HardMarginLeft = GetDeviceCaps(hDC, PHYSICALOFFSETX);
        HardMarginTop = GetDeviceCaps(hDC, PHYSICALOFFSETY);
        Width = GetDeviceCaps(hDC, HORZRES);
        Height = GetDeviceCaps(hDC, VERTRES);
 
        e.Graphics.ReleaseHdc(hDC);
 
        HardMarginLeft = (int)(HardMarginLeft * 100.0 / e.Graphics.DpiX);
        HardMarginTop = (int)(HardMarginTop * 100.0 / e.Graphics.DpiY);
        Width = (int)(Width * 100.0 / e.Graphics.DpiX);
        Height = (int)(Height * 100.0 / e.Graphics.DpiY);
    }
    #endregion
}
 
P

Peter Duniho

First off, sorry for non-text mode of this post - the code looks like
crap that
way.

The code looks like crap which way? As text? I find plain text to be the
very *best* way to post code.
I figured the problem out with the help of an individual from MS Support.
Basically forget about all the values in e.PageBounds or
e.PageSettings.PrintableArea.
You have to drop down to the WinAPI to get what you need.

Forgive me my skepticism, but I believe MS Support is steering you wrong.
It's inconceivable to me that they would bother to include PrintableArea
as a property in the PageSettings when it's not a useful way to know where
on the page you can print.

I know all about the native Windows printing API methods to get the
information you seek (in fact, I know it much better than I know the .NET
stuff, since I've barely used the .NET stuff). But it really doesn't seem
to me that it should be necessary to go that route.

Anyway, I'm glad you got something that you feel addresses your problem in
the most appropriate way.

Pete
 

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

Similar Threads


Top