title: Bitmap to PrinterDocument page resolution is not matching
body: <p>The problem I am having is getting the new Bitmap to sync properly with the size of the printer output using <code>DrawImageUnscaled()</code> inside the <code>PrinterDocument</code>'s <code>PrintPage</code> event.</p>
  
  <p>I got the idea to render an image as a page collection from the comments on another post where I asked how to work with the printer in a more traditional style ( <code>NewPage</code>, drawing items <em>before</em> calling <code>Print</code>, etc  ) which does not exist within the .NET framework. On my first attempt to use an Image collection, I noticed their was some graininess when using <code>.DrawImage()</code> even after setting the Bitmap Dpi to the same Dpi as the printer object, which I found was discoverable without printing using the <code>PrinterSettings.CreateMeasurementGraphics()</code> Graphics object through trial and error (lots of error.</p>
  
  <p>The result of this endeavor thus far, is the following class (it has some 'test' code where I have been playing around, but I have cleaned up most of it so it is more presentable here)</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading.Tasks;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Windows.Forms;
  using System.Windows.Forms.Design;
  
  using PdfFileWriter;
  using System.Drawing.Printing;
  using System.ComponentModel;
  
  using System.IO;
  
  class PDF : PrintDocument {
      /// &lt;summary&gt;
      /// Logo to display on invoice
      /// &lt;/summary&gt;
      public Image Logo { get; set; }
  
      /// &lt;summary&gt;
      /// Current X position on canvas
      /// &lt;/summary&gt;
      public int X { get; set; }
  
      /// &lt;summary&gt;
      /// Current Y position on canvas
      /// &lt;/summary&gt;
      public int Y { get; set; }
  
      /// &lt;summary&gt;
      /// Set the folder where backups, downloads, etc will be stored or retrieved from
      /// &lt;/summary&gt;
      [Editor( typeof( System.Windows.Forms.Design.FolderNameEditor ), typeof( System.Drawing.Design.UITypeEditor ) )]
      public string Folder { get { return directory; } set { directory=value; } }
  
      /// &lt;summary&gt;
      /// Current font used to print
      /// &lt;/summary&gt;
      public Font Font { get; set; }
  
      /// &lt;summary&gt;
      /// Current font color
      /// &lt;/summary&gt;
      public Color ForeColor { get; set; }
  
      private int CurrentPagePrinting { get; set; }
  
      /// &lt;summary&gt;
      /// Set printer margins
      /// &lt;/summary&gt;
      public Margins PrintMargins {
          get { return DefaultPageSettings.Margins; }
          set { DefaultPageSettings.Margins = value; }
      }
  
      /// &lt;summary&gt;
      /// Pages drawn in document
      /// &lt;/summary&gt;
      private List&lt;Image&gt; Pages;
  
      /// &lt;summary&gt;
      /// The current selected page number. 0 if nothing selected
      /// &lt;/summary&gt;
      private int CurrentPage;
  
      /// &lt;summary&gt;
      /// The current working directory to save files to
      /// &lt;/summary&gt;
      private string directory;
  
      /// &lt;summary&gt;
      /// The currently chosen filename
      /// &lt;/summary&gt;
      private string file;
  
      /// &lt;summary&gt;
      /// Public acceisble object to all paperSizes as set
      /// &lt;/summary&gt;
      public List&lt;PrintPaperSize&gt; paperSizes { get; private set; }
  
      /// &lt;summary&gt;
      /// Object for holding papersizes
      /// &lt;/summary&gt;
      public class PrintPaperSize {
          public string Name { get; set; }
          public double Height { get; set; }
          public double Width { get; set; }
  
          public PrintPaperSize() {
              Height = 0;
              Width = 0;
              Name = "";
          }
      }
  
      /// &lt;summary&gt;
      /// Current papersize selected. used for some calculations
      /// &lt;/summary&gt;
      public PrintPaperSize CurrentPaperSize { get; private set; }
  
      public PDF() {
          // set the file name without extension to something safe
          file = (string)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds.ToString();
  
          // set the save directory to MyDocuments
          directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
          CurrentPage = 0;
  
          // initialize pages array
          Pages = new List&lt;Image&gt;();
  
          // Set the initial font and color
          Font = new System.Drawing.Font("Arial", (float)11.25);
          ForeColor = Color.Black;
  
          // set the printer to Microsoft's PDF printer and generate and ensure it will save to a file
          PrinterSettings = new PrinterSettings() {
              PrinterName = "Microsoft Print to PDF",
              PrintToFile = true,
              PrintFileName = Path.Combine(directory, file + ".pdf"),
          };
  
          // hide the notice 'printing' while spooling job.
          PrintController = new StandardPrintController();
  
          // set the printer quality to maximum so we can use this for getting the dpi at this setting
          DefaultPageSettings.PrinterResolution.Kind = PrinterResolutionKind.High;
  
          // store all paper sizes at 1 dpi [ reference: https://social.msdn.microsoft.com/Forums/vstudio/en-US/05169a47-04d5-4890-9b0a-7ad11a6a87f2/need-pixel-width-for-paper-sizes-a4-a5-executive-letter-legal-executive?forum=csharpgeneral ]
          paperSizes = new List&lt;PrintPaperSize&gt;();
          foreach ( PaperSize P in PrinterSettings.PaperSizes ) {
              double W=P.Width/100.0;
              double H=P.Height/100.0;
  
              paperSizes.Add(
                  new PrintPaperSize() {
                      Height = H,
                      Width = W,
                      Name = P.PaperName
                  }
              );
  
              if ( P.PaperName=="Letter" ) {
                  CurrentPaperSize = paperSizes[paperSizes.Count-1];
              }
          }
  
          // setup the initial page type, orientation, margins, 
          using ( Graphics g=PrinterSettings.CreateMeasurementGraphics() ) {
              DefaultPageSettings = new PageSettings(PrinterSettings) {
                  PaperSize=new PaperSize( CurrentPaperSize.Name, (Int32)(CurrentPaperSize.Width*g.DpiX), (Int32)(CurrentPaperSize.Height*g.DpiY) ),
                  Landscape = false,
                  Margins = new Margins(left: 10, right: 10, top: 10, bottom: 10),
                  PrinterResolution=new PrinterResolution() {
                      Kind = PrinterResolutionKind.High
                  }
              };
          }
  
          // constrain print within margins
          OriginAtMargins = true;
      }
  
  
      public void SetPaperSize( PaperKind paperSize ) {
          // TODO: Use Linq on 
      }
  
      /// &lt;summary&gt;
      /// Get specific page
      /// &lt;/summary&gt;
      /// &lt;param name="page"&gt;page number. 1 based array&lt;/param&gt;
      /// &lt;returns&gt;&lt;/returns&gt;
      public Image GetPage( int page ) {
          int p = page - 1;
          if ( p&lt;0||p&gt;Pages.Count ) { return null; }
          return Pages[p];
      }
  
      /// &lt;summary&gt;
      /// Get the current page
      /// &lt;/summary&gt;
      /// &lt;returns&gt;Image&lt;/returns&gt;
       public Image GetCurrentPage() {
          return GetPage(CurrentPage);
      }
  
      /// &lt;summary&gt;
      /// Before printing starts
      /// &lt;/summary&gt;
      /// &lt;param name="e"&gt;PrintEventArgs&lt;/param&gt;
      protected override void OnBeginPrint( PrintEventArgs e ) {
           CurrentPagePrinting=0;
           base.OnBeginPrint( e );
      }
  
      /// &lt;summary&gt;
      /// Print page event
      /// &lt;/summary&gt;
      /// &lt;param name="e"&gt;PrintPageEventArgs&lt;/param&gt;
      protected override void OnPrintPage( PrintPageEventArgs e ) {
          CurrentPagePrinting++;
  
          // if page count is max exit print routine
          if ( CurrentPagePrinting&gt;=Pages.Count ) { e.HasMorePages=false; base.OnPrintPage( e ); return; }
  
          // ensure high resolution / clarity of image so text doesn't fuzz
          e.Graphics.CompositingMode=CompositingMode.SourceOver;
          e.Graphics.CompositingQuality=CompositingQuality.HighQuality;
  
          // Draw image and respect margins (unscaled in addition to the above so text doesn't fuzz)
          e.Graphics.DrawImageUnscaled(
              Pages[CurrentPagePrinting-1],
              new Point(
                  DefaultPageSettings.Margins.Top,
                  DefaultPageSettings.Margins.Left
              )
          );
          base.OnPrintPage( e );
      }
  
      /// &lt;summary&gt;
      /// After printing has been completed
      /// &lt;/summary&gt;
      /// &lt;param name="e"&gt;PrintEventArgs&lt;/param&gt;
      protected override void OnEndPrint( PrintEventArgs e ) {
          base.OnEndPrint( e );
      }
  
      /// &lt;summary&gt;
      /// Add a new page to the document
      /// &lt;/summary&gt;
      public void NewPage() {
          // Add a new page to the page collection and set it as the current page
  
          Bitmap bmp;
          using(Graphics g = PrinterSettings.CreateMeasurementGraphics()) {
              float dpiscaleX;
              float dpiscaleY;
  
              // measure default bitmap dpi on this system and use to calculate print dpi
              using ( Bitmap b=new Bitmap( 1, 1 ) ) {
                  dpiscaleX = b.HorizontalResolution;
                  dpiscaleY = b.VerticalResolution;
              };
  
              bmp = new Bitmap( 
                  (Int32)(((DefaultPageSettings.PrintableArea.Width-( DefaultPageSettings.Margins.Left+DefaultPageSettings.Margins.Right )) / dpiscaleX) * g.DpiX),
                  (Int32)(((DefaultPageSettings.PrintableArea.Height-( DefaultPageSettings.Margins.Top+DefaultPageSettings.Margins.Bottom )) / dpiscaleY) * g.DpiY)
              );
              bmp.SetResolution(g.DpiX, g.DpiY);
          }
          Pages.Add( bmp );
          CurrentPage++;
      }
  
      /// &lt;summary&gt;
      /// Add a new string to the current page
      /// &lt;/summary&gt;
      /// &lt;param name="text"&gt;The string to print&lt;/param&gt;
      /// &lt;param name="align"&gt;Optional alignment of the string&lt;/param&gt;
      public void DrawString(string text, System.Windows.TextAlignment align = System.Windows.TextAlignment.Left ) {
          // add string to document
          using ( Graphics g=Graphics.FromImage( Pages[CurrentPage - 1] ) ) {
              g.CompositingQuality = CompositingQuality.HighQuality;
              switch ( align ) {
                  case System.Windows.TextAlignment.Left:
                  case System.Windows.TextAlignment.Justify:
                      g.DrawString( text, Font, new SolidBrush( ForeColor ), new PointF( X, Y ) );
                      Y+=(Int32)g.MeasureString( "X", Font ).Height;
                      break;
                  case System.Windows.TextAlignment.Right:
                      g.DrawString( text, Font, new SolidBrush( ForeColor ), new PointF( Pages[CurrentPage - 1].Width - g.MeasureString( text, Font ).Width, Y ) );
                      Y += (Int32)g.MeasureString( "X", Font ).Height;
                      break;
                  case System.Windows.TextAlignment.Center:
                      g.DrawString( text, Font, new SolidBrush( ForeColor ), new PointF( ( Pages[CurrentPage-1].Width+g.MeasureString( text, Font ).Width )/2, Y ) );
                      Y+=(Int32)g.MeasureString( "X", Font ).Height;
                      break;
              }
          }
      }
  }
  </code></pre>
  
  <h1>Illustration of PDF output</h1>
  
  <p><a href="https://i.stack.imgur.com/kAtL2.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/kAtL2.png" alt="Demonstration of Dpi"></a></p>
  
  <p>As you can see, the canvas (<code>Page</code> property in my class), goes out of bounds. If I draw the image using just <code>.DrawImage()</code> the scaling stretches it and it just looks grainy, so I must use <code>.DrawImageUnscaled()</code></p>
  
  <p>The lines above in order are the result of the following code :</p>
  
  <pre><code>// Initialize the custom print class
  PDF p = new PDF();
  
  // Add a new page to the document
  p.NewPage();
  
  // Draw some strings.  p.Y value is automatically incremented
  p.DrawString( "Hello" );
  p.DrawString( "Hello", System.Windows.TextAlignment.Right );
  p.DrawString( "Hello", System.Windows.TextAlignment.Center );
  p.DrawString( "Hello pure awesomeness" );
  
  // Uncomment the following and add a picture box to the form
  // pictureBox1.Height = 1100;
  // pictureBox1.Width = 850;
  // pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
  // pictureBox1.Image = p.GetCurrentPage();
  
  // Send all pages to the "printer"
  p.Print();
  </code></pre>
  
  <p>If you uncomment the <code>pictureBox1</code> lines, and comment out <code>p.Print()</code>, the result is correct (keeping in mind the image is smaller than the printdocument page due to the margins being set for the page.</p>
  
  <h1>Illustration of PictureBox output</h1>
  
  <p><a href="https://i.stack.imgur.com/Klhvl.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Klhvl.png" alt="Demonstration of Dpi in PictureBox"></a></p>
  
  <p>And if you use the following code (in place of the above <code>pictureBox</code> code) which is functionally equivalent to the Dpi scaling inside the class, everything shows up properly in the <code>pictureBox</code> (just a lot larger as my 'High' setting resolves to 600 DPI where a newly created Bitmap is at 72 DPI before calling the <code>SetResolution()</code> method on the Image.</p>
  
  <pre><code>Image img = p.GetCurrentPage();
  
  pictureBox1.Height=(Int32)(p.CurrentPaperSize.Height*img.VerticalResolution);
  pictureBox1.Width = (Int32)(p.CurrentPaperSize.Width*img.HorizontalResolution);
  pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
  pictureBox1.Image = img;
  </code></pre>