Is the unit in Windows Forms printing always 100dpi point? - c#

What is the unit of the coordinate system used in Windows Forms printing using the PrintDocument class? This information is needed in order to print something at a specific position and with a specific size.
In the PrintPage event, the PrintPageEventArgs instance has the properties Graphics and PageBounds. They seem to use the same coordinate system.
For an A4 portrait sheet, PageBounds returns a size of 827 by 1169. Given an A4 sheet is 210mm by 297mm, the unit Graphics / PageBounds unit seems to be pixels/points with 100dpi. (827 / 210 * 25.4 = 100.0278, 1169 / 297 * 25.4 = 99.9751).
Using 100dpi to scale and position the objects, the drawing result is correct. But is it always 100dpi? Or how can I query the unit?
(Querying Graphics.DpiX does not work. It returns 600dpi, which is the printer DPI but not the coordinate system DPI.)
private void PrintButton_Click(object sender, EventArgs e)
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(PrintDocument_PrintPage);
pd.Print();
}
private void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
{
Rectangle bounds = e.PageBounds; // For A4 portrait sheet: {X = 0 Y = 0 Width = 827 Height = 1169}
float dpi = e.Graphics.DpiX; // 600
DrawIt(e.Graphics);
}

Thanks to Jimi who pointed out that the unit is Display. The short answer is: It's always 100dpi for printing.
The Graphics instance uses GraphicsUnit.Display as the PageUnit. And for printers, this is 1/100 inch for printers, i.e. 100dpi. The documentation says "typically" but this probably refers to the video displays.
It also coincides with PrinterUnit.Display, which is always 0.01in.
As the Graphics measurements are also consistent with PageBounds, I can probably safely assume that PageBounds and other PrintPageEventArgs properties also use display units for printers with 100dpi. It's not documented though.

Related

Is there a way to place a vector drawing in the PrintPage-event of printDocument?

I am writing an application that creates "print" files for a laser engraver.
It is using specific RGB values for setting the laser speed and power (e.g. RGB(0,0,0) for setting 1, RGB(255,0,0) for setting 2). It will ignore any element where the RGB value is just a little bit off.
As we are printing nameplates with this device, I need as result a PDF file with many nameplates on it. I have coded a "nameplate designer" where you can design the nameplate (e.g. line in RGB(0,0,0) at x=10mm, y=10mm ....).
My initial thought was to code an interpreter which does return a Bitmap and place these bitmaps on the print file. It works, it looks good, BUT due to the pixel graphics, the colors are not 100% accurate at the edges.
should be all RGB(0,255,0)
It is 100% accurate when I put the interpreter in the PrintPage-event of printDocument, but I would like to maintain the possibility to export the image as Bitmap when needed (e.g. a customer asks for a preview). Now could I just copy the interpreter (1 for Bitmap, 1 for PrintPage), but for maintainability it would be better to have this code just once for both output ways.
I tried with Metafile, but don't know how to place it in the PrintPage-event properly (page is just blank currently) // or how to create the Metafile properly
private Metafile printMF()
{
Graphics grfx = CreateGraphics();
MemoryStream ms = new MemoryStream();
IntPtr ipHdc = grfx.GetHdc();
Metafile mf = new Metafile(ms, ipHdc);
grfx.ReleaseHdc(ipHdc);
grfx.Dispose();
grfx = Graphics.FromImage(mf);
grfx.DrawLine(new Pen(Brushes.Black), 10, 10, 300, 10);
grfx.DrawRectangle(new Pen(Brushes.Black), new Rectangle(20, 20, 400, 400));
return mf;
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Metafile mf = printMF();
e.Graphics.DrawImage(mf, 20, 20,500,500);
}
Is there any obvious mistake or is it just impossible and I have to create two interpreters.
Thanks a lot for your help!

My e.graphics.drawimage(mybitmap,200,200) is drawing my bitmap at 200,222. Why?

I initialize my bitmap here
Bitmap x = new Bitmap("andgateopen.bmp");
and it's size is 68x44. When I try to draw it here:
private void toolbox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(x, 200, 200);
}
It shows up at 200,222 on my screen. Why is this happening? If I draw the bitmap using e.Graphics.DrawImage(x, 200, 178) then it shows up correctly. Where are these 22 units?
Solved (Sort of) - It's the title bar. Since it's always a consistent 23 units, I can account for it consistently. Thanks to TaW for help.
Perhaps your Graphics instance doesn't have an identity Transform.
e.Graphics.Transform = new Matrix();
It might also depend on which units you are drawing in. Device, World or Point.

How to re-size QRCode image without losing resolution?

I'm generating a QR code using the QR code library "MessagingToolkit.QRCode.Codec" but it's generating a large size QR code and I'm trying to resize it to 70x70px, however the generated image's resolution is not good.
protected void BtnPrint_Click(object sender, EventArgs e)
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += PrintPage;
pd.Print();
}
private void PrintPage(object o, PrintPageEventArgs e)
{
QRCodeEncoder encoder = new QRCodeEncoder();
String EmployeeId= this.Request.QueryString.Get("EmployeeId").ToString();
Bitmap img = new Bitmap (encoder.Encode(EmployeeId), new Size(70,70));
Point loc = new Point(100, 100);
e.Graphics.DrawImage(img, loc);
}
How can I resize this QRCode without losing the resolution ?
EDIT:
Is it possible to create the QRCode from the beginning with the required size ?
You can
encoder.QRCodeScale = (number).
It controls the "scale" used to create the image.
The "scale" controls directly the size of the dots (in pixels). A scale of 1 means each dot is 1 pixel wide/high.
Note that there is a bug in the size calculation, so the image could be cropped of 1 pixel both in width and in height (if you look, the last row/column are blank)
QRCodeEncoder encoder = new QRCodeEncoder();
encoder.QRCodeScale = 1;
using (var bmp = encoder.Encode(EmployeeId))
{
// There is a bug in QRCodeEncoder, so that it will add an
// additional "blank" row/column
var bmp2 = bmp.Clone(new Rectangle(0, 0, bmp.Width - 1, bmp.Height - 1), bmp.PixelFormat))
// use bmp2
}
If you compare these QR Codes we have created in a graphics program you will see the difference in resolution. The first (2488) has a scale of 35 and the second (2490) has a scale of 40. What this means is that each module is either 35 or 40 pixels square. If you were making a QR Code for a billboard (as opposed to say a business card) you would need to ensure that the resolution of each module was high enough that it doesn't 'pixelate' (blur) when printed really large. The down side is that you are creating a larger image which will take up more memory if (like Codacast) you are creating (and saving) lots of them - hope this helps :-)
http://www.codacast.com/qrimages/2488.png
http://www.codacast.com/qrimages/2490.png

PrintDocument always has margin

I am having a problem when using PrintDocument with margins.
No matter what I do there is always a margin around everything I print, this means that nothing is aligned where it needs to be.
Here is the code I am using to create the PrintDocument
public void Print()
{
PrintDocument printDocument = new PrintDocument();
printDocument.DefaultPageSettings.PaperSize = new PaperSize("A5",583,827);
printDocument.OriginAtMargins = true;
printDocument.DefaultPageSettings.Margins.Top = 0;
printDocument.DefaultPageSettings.Margins.Left = 0;
printDocument.DefaultPageSettings.Margins.Right = 0;
printDocument.DefaultPageSettings.Margins.Bottom = 0;
if (!string.IsNullOrWhiteSpace(PrinterName))
{
printDocument.PrinterSettings.PrinterName = PrinterName;
}
printDocument.PrintController = new StandardPrintController();
printDocument.PrintPage += On_PrintPage;
printDocument.Print();
}
The On_PrintPage method, has various calls to e.Graphics.Draw... methods.
How can I make it so that something I print at 0,0 will print in the very top left of the page. I know that if the printer cannot print that far to the edge of the page it will be blank, however it should do that rather than print 0,0 not in the top left of the page.
I'm really lost here
interestingly the print function is too late to set most of the properties and would only apply to subsequent pages
you need to use PrintDocument.QueryPageSettings event instead and set the properties there and I always set the page settings not just defaults. then drawing at 0,0 should be as close as you can get (printer + driver allows)
The OriginAtMargins property of the PrintDocument, when set to true, it will sets the origin of the provided Graphics object at the top-left corner of the margin. From the MSDN document Remarks section:
Calculating the area available to print requires knowing the physical
size of the paper, the margins for the page, and the location of the
Graphics object origin. When OriginAtMargins is true, the Graphics
object location takes into account the PageSettings.Margins property
value and the printable area of the page. When OriginAtMargins is
false, only the printable area of the page is used to determine the
location of the Graphics object origin, the PageSettings.Margins value
is ignored.
Set it to false, to change it to the top-left corner of the printable area. If you need it to be at the top-left corner of the page, you will need to transform the graphics object to mach that coordinate; there isn't really a way to get around this:
void PrintDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
var printArea = e.PageSettings.PrintableArea;
e.Graphics.TranslateTransform(-printArea.X, -printArea.Y);
// Build up the page here.
}
If you want to print landscape pages as well, this becomes tricky, as none of the properties of PageSettings are affected by this setting. This requires to know the angle the page is rotated by—as PrintableArea isn't always centered on the page—which can be acquired with PageSettings.PrinterSettings.LandscapeAngle. The value can only be either 90 or 270, and the code would look like this:
void PrintDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
var printArea = e.PageSettings.PrintableArea;
if (e.PageSettings.Landscape)
{
var pageSize = e.PageSettings.PageSize;
switch (e.PageSettings.PrinterSettings.LandscapeAngle)
{
case 90:
e.Graphics.TranslateTransform(
dx: -printArea.Y,
dy: -(pageSize.Width - (printArea.X + printArea.Width)));
break;
case 270:
e.Graphics.TranslateTransform(
dx: -(pageSize.Height - (printArea.Y + printArea.Height)),
dy: -printArea.X);
break;
}
}
else
{
e.Graphics.TranslateTransform(-printArea.X, -printArea.Y);
}
// Build up the page here.
}
If you later want to use another transformation on a portion of the page, do not forget to use var gs = e.Save() before transforming and e.Restore(gs) to restore that transformation, instead of calling e.ResetTransform(), as the later will reset the transformation you did in the beginning.

Print in .NET - Conversion from millimeter to pixel

How can I convert user input from millimeter to pixels so that it's printed on the right position of the page?
I use the following code:
private void document_PrintPage(object sender, PrintPageEventArgs e)
{
float dpiX = e.Graphics.DpiX;
float dpiY = e.Graphics.DpiY;
Point p = new Point(mmToPixel(float.Parse(edtBorderLeft.Text), dpiX),
mmToPixel(float.Parse(edtBorderTop.Text), dpiY));
e.Graphics.DrawImage(testImage, p);
}
private int mmToPixel(float mm, float dpi)
{
return (int)Math.Round((mm / 25.4) * dpi);
}
edtBorderLeft.Text got the value of "9.5" and edtBorderTop.Text the value of "21,5". These values are millimeters. If I check the output with this code:
private void printPage()
{
PrintDialog dialog = new PrintDialog();
dialog.Document = document;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
PrintPreviewDialog preview = new PrintPreviewDialog();
preview.Document = document;
preview.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
preview.Show();
//document.Print();
}
}
It displays the image nearly in the center of the page. A calculation example:
mmToPixel(float.Parse(edtBorderLeft.Text), dpiX)
edtBorderLeft.Text = "9.5"
dpiX = 600;
returns: 224
How can I calculate the right point for the printed image?
I found a solution. You can change the page unit with the following code. So I don't need a conversion:
e.Graphics.PageUnit = GraphicsUnit.Millimeter;
or
e.Graphics.PageUnit = GraphicsUnit.Pixel;
and I can use the code above.
Just to add a little explanation. By default Graphics.PageUhit is set to "Display". For a screen display this usually means 96 pixels per inch, for a printer it is 100 dots per inch. This info is buried in MSDN somehwere but is hard to find.
Therefore for a printer, instead of using dpiX/dpiY you could assume a value of 100, but it is probably safer to set the units to millimeters.

Categories

Resources