简体   繁体   中英

Print a Form at higher dpi than screen resolution

Problem:

We need help with how to use WinForms' ability to auto-scale to different DPI's to allow us to print our Forms at 600dpi, rather than at the screen DPI.

For What-You-See-Is-What-You-Get printing, we have been simply taking our nicely laid out window and printing it (turning off scrollbars and buttons and such). That works great EXCEPT for one thing: it comes out at 96dpi or 120dpi (whatever is the screen resolution)… either of which look grainy and unprofessional (our customers are complaining). And although it is as readable as what would be on the screen, you expect printed documents to be MORE readable than on-screen… you expect to be able to see additional details, to be able to read smaller text, etc.

Alternatives considered:

Given we have auto-scaling working great, such that our window looks good in 96dpi, 120dpi, 144 dpi, etc., we were hoping we could just draw our window at 600dpi and then print that.

OR, we looked at drawing the window off-screen 5-6x larger than normal such that we have the same number of pixels as 600dpi, but at 96 or 120 dpi… but then drawing that giant window to the printed page at 300 or 600 dpi (whatever the printer is).

If you can tell us how to do either of those alternatives, OR if you can give us a different way to accomplish our goal, then we would greatly appreciate it.

Current code:

In case it matters, our Form consists of a FlowLayoutPanel laying other smaller FlowLayoutPanels into columns, those smaller FlowLayoutPanels laying out a single column of TextBoxes, RichTextBoxes, a third-party RichTextEditor, PictureBoxes, and DataGridViews. We use a class derived from PrintDocument implementing OnBeginPrint, OnPrintPage, and OnEndPrint. In OnPrintPage, it manipulates our normal window off-screen (below and right of the actual screens) to fit the page size, then asks our main panel (the top FlowLayoutPanel) to DrawToBitmap, then uses the Graphics object passed into the PrintEventArgs to DrawImage that Bitmap. We also use Graphics.DrawString to apply a footer to each page. The main code:

                    using (Bitmap bm = new Bitmap(sz.Width, sz.Height))
                    {
                        Rectangle rect = new Rectangle(0, 0, sz.Width, sz.Height);
                        mp.DrawToBitmap(bm, rect);
                        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; // so footer is anti-aliased
                        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;  // so when we scale up, we smooth out the jaggies somewhat
                        e.Graphics.DrawImage(bm, this.MarginBounds, rect, GraphicsUnit.Pixel);
                        if (this.Footer != null)
                            e.Graphics.DrawImage(this.Footer, this.FooterLocation);
                        if (!string.IsNullOrEmpty(pageNumber))
                        {
                            e.Graphics.DrawString(pageNumber, KBStyle.Normal.Font, Brushes.Black,
                                                  this.MarginBounds.X, this.FooterLocation.Y + FooterOffset);
                        }
                    }

How should we do this to get 600dpi to the printed page? (Or even 300 dpi would be great!)

When we print this, it looks far better when printed from a 120dpi machine than when printed from a 96dpi machine, hence why we know it is printing at screen resolution. But also makes us wonder if there's some simple way to tell it “this Form should draw at 600 dpi” and then all the rest of the code above just works.

Note: if we grab an EMF (Enhanced Metafile) and print it to the printer in the code above, that EMF comes out at 600dpi. Unfortunately, we haven't found a DrawToEMF method that we can call on the FlowLayoutPanel instead of DrawToBitmap. Changing the Bitmap to be 600dpi doesn't help… the DrawToBitmap method still seems to draw the bitmap at screen resolution.

Thanks!!

Okay, I have figured this out... and it works great!

I still don't know how to create a form at 300dpi and use the auto-scaling functionality.

BUT…

I have proven that if you create the window 3.125x larger than needed at 96 dpi, and scale the font up 3.125x, and so on, such that everything is the pixel count you'd need to be at 300dpi, even though your screen is at 96dpi, then you can use the normal Control.DrawToBitmap() functionality to turn that into a Bitmap, and then you can use the GDI Graphics.DrawImage(thatGiantBitmap, giantSrcRect, pageSizeDestRect) to the printer Graphics object, and it will map those giant 96dpi pixels to the page-size 300dpi pixels, giving you a 300dpi print. Perfect.

For any of our windows that support resizing AND let our users zoom the contents arbitrarily, then printing What-You-See-Is-What-You-Get is easy:

In OnBeginPrint of your PrintDocument, do:

(1) Optionally dup the Form so as to not mess with what the user is looking at

(2) Move the Form you want to print off-screen (below and right of all your screens)

(3) Set the Form so that it is allowed to grow bigger than the screen size (by default WinForms won't grow larger than the screen)

(4) Divide 300 dpi by your screen dpi to get the growth factor

(5) Grow your Form by growth factor

(6) Zoom its contents by growth factor (if they don't auto-scale / auto-zoom with the Form's size)

In OnPrintPage of your PrintDocument, do:

(7) On whatever Control in your Form you want to print, do DrawToBitmap() to a Bitmap the size of that Control

(8) On the e.Graphics do DrawImage(thatGiantBitmap, giantSrcRect, pageSizeDestRect)

That DrawImage call will draw at the printer's resolution, if you have that many pixels in your thatGiantBitmap. In this case, I computed the Bitmap to give the number of pixels needed for 300 dpi, so I will get 300 dpi print-outs even if the printer is 600 dpi. If you need full 600 dpi, just use 600 dpi in step 4's calculation.

Total guess here, but what about using CSS? I'm guessing at how much you want to scale, and don't know how you would know what scale the printer is. Using the print media query makes this work for print but leaves the screen view alone.

@media print {
  * {
    transform: scale(2000px,2000px);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM