Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Printer Margins, Part 2


Printer Margins, Part 2

In the previous newsletter, our goal was to print within the real page boundary of a printer’s physical capabilities, which lead to developing a method called “GetRealPageBounds” that works in the face of preview printing and alternating scaling. While the page bounds are useful for headers and footers, for the bulk of the printed content, however, you should be printing inside of the MarginBounds rectangle:

void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {<br>  Graphics g = e.Graphics;<br>  g.DrawString(..., e.MarginBounds);<br>}

Unfortunately, because the MarginBounds is offset from the PageBounds and the PageBounds is offset to stay inside the printable region of the page, the MarginBounds will often be lined up at offsets that don't match the user-specified margins along the edge of the page.

For example, on my Hewlett-Packard LaserJet 2100, the left edge of the PageBounds rectangle is actually 1/4 of an inch in from the left edge and the top edge is 1/8th of an inch down from the top. This affects the MarginBounds, lining up the 1 inch margin I expect at 1-1/4 inches from the left edge of the page. This poses a problem since neither the PageBounds nor the VisibleClipBounds nor any other information provided by WinForms will actually tell you how much the offset of the PageBounds is from the actual edge of the paper.

To get the physical offsets, you need to turn to interop with Win32 and the GetDeviceCaps function. Using that, you can get the printer's physical X and Y offset from the top left and adjust the margins appropriately. However, the X and Y offset will be in printer coordinates, which may not be the same units as the MarginBounds, so you'll have to convert those units as well. The following helper methods do all of that work:

[System.Runtime.InteropServices.DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, DeviceCapsIndex index);

enum DeviceCapsIndex {
  PhysicalOffsetX = 112,
  PhysicalOffsetY = 113,
}

// Adjust MarginBounds rectangle when printing based
// on the physical characteristics of the printer
static
  Rectangle GetRealMarginBounds(PrintPageEventArgs e, bool preview) {
  if( preview ) return e.MarginBounds;

  int cx = 0;
  int cy = 0;
  IntPtr hdc = e.Graphics.GetHdc();

  try {
    // Both of these come back as device units and are not
    // scaled to 1/100th of an inch
    cx = GetDeviceCaps(hdc, DeviceCapsIndex.PhysicalOffsetX);
    cy = GetDeviceCaps(hdc, DeviceCapsIndex.PhysicalOffsetY);
  }
  finally {
    e.Graphics.ReleaseHdc(hdc);
  }

  // Create the real margin bounds by scaling the offset
  // by the printer resolution and then rescaling it
  // back to 1/100th of an inch
  Rectangle marginBounds = e.MarginBounds;
  int dpiX = (int)e.Graphics.DpiX;
  int dpiY = (int)e.Graphics.DpiY;
  marginBounds.Offset(-cx * 100 / dpiX , -cy * 100 / dpiY);
  return marginBounds;
}
 

The GetRealMarginBounds method will take preview mode into account and, when using a real printer, adjust the MarginBounds using the physical offsets, always returning a rectangle in the same units. With this in place, you can safely print inside the margins based on the edges of the paper, as you'd expect:

 void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {
  ...
  g.DrawString(..., GetRealMarginBounds(e));
}
 

As an alternative to using these helper functions, the .NET 1.1 Framework provides a property on the PrintDocument called OriginAtMargins. This property defaults to False, but setting it to True sets the offset of the PageBounds rectangle to be at the margin offset from the physical edge of the page, giving you the ability to print at the appropriate margins using the PageBounds rectangle. However, this property doesn't have any affect in preview mode, doesn't adjust the PageBounds size and keeps the MarginBounds as offset from the now further offset PageBounds, so I don't find it particularly useful when compared with the GetRealPageBounds and GetRealMarginBounds helper methods.

This material is excerpted from the forthcoming Addison-Wesley title Windows Forms Programming in C# by Chris Sells (0321116208). Please note the material presented here is an initial DRAFT of what will appear in the published book.


Chris Sells in an independent consultant specializing in distributed applications in .NET and COM, as well as an instructor for DevelopMentor. He's written several books including ATL Internals, which is currently being updated for ATL7. He's also working on Essential Windows Forms for Addison-Wesley and Mastering Visual Studio .NET for O'Reilly. In his free time, Chris directs the Genghis source-available project. If you enjoy this newsletter, you may be interested in theSellsBrothers News, which notifies subscribers of the various projects that Chris is working on. Subscribe or browse the archives at http://www.sellsbrothers.com/newsletter.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.