I want to know the printing size of a string in inches. i.e. what will be the length (and width) of string "Test String" when printed on a paper. I can get the length in pixels using the Graphics object and MeasureString method. But don't know how to convert pixels into inches on paper.
I checked this SO question but can't find the solution.
The resolution of a digital graphic is its pixel dimensions.
The size of the printed graphic is dependent of the printer settings.
If you print a picture of 300 x 300
pixels at 300dpi on a printer the
picture becomes 1 x 1 inch. ( 300 / 300 )
If you print a picture of 300 x 300
pixels at 10dpi on a printer the
picture becomes 30 x 30 inch. ( 300 / 10 )
If you print a picture of 300 x 300
pixels at 1000dpi on a printer the
picture becomes 0,3 x 0,3 inch. (300 / 1000 )
However, the exact size depends on the accuracy of the printer (paper feeding, servo of the printer head etc.)
An excellent reference is found here: DPI has NOTHING to do with digital image quality!.
The Dpi of the display has NOTHING to do with this
I know this is an old question, but a quazi solution for it. I have tested my solution a few times and it seems to be accurate but not precise. The basis of this code is directly from an MSDN example and this answer on SO, the only thing I am doing differently is changing the units from points to inches.
MSDN Graphics.MeasureString()
private float GetTextWidth(string fontFace, float fontSize, string text)
{
SizeF size = new SizeF();
Font font = new Font(fontFace, fontSize);
//Using a Bitmap object for frame of reference in order to get to a Graphics object
using (Bitmap b = new Bitmap(1, 1))
{
//Graphics object allows string measurement
using (Graphics g = Graphics.FromImage(b))
{
//change the units to inches
g.PageUnit = GraphicsUnit.Inch;
//Perform the measurement
size = g.MeasureString(text, font);
}
}
//Print to console if you want
//Console.WriteLine(size.Width);
return size.Width;
}
I tested this out with the following inputs:
("Arial", 10, "Page 1 of 1") and I got 0.777972 inches
If you read the Remarks of the MSDN link above you will see that there is a little extra space padded onto that figure. You have to be aware that this happens:
The MeasureString method is designed for use with individual strings
and includes a small amount of extra space before and after the string
to allow for overhanging glyphs.
When I measured this on a piece of paper end to end I got about 0.625, that is a difference of 0.152972 which is essentially ~20% padding. I'm guessing it is distributed on either side evenly.
Also be careful with which Fonts you use - for example Helvetica is not a supported font by default, therefore the Font object will default to Arial if it can't find your font.
This is not possible, unless you know both the printer size and display size in hard numbers.
Say you're printing a full screen handout. On a 30 inch monitor, 1 pixel may be 1 unit large on paper. But on a 7 inch netbook, a pixel will be 2 units large on paper - twice the size, even though they are both one pixel on the electronic display.
Simply put, this measurement is not possible unless you are working with something fixed, like an iPhone's display size (which is well known), or your own hardware whose screen size you have exact measurements to.
Related
I created an extended RichTextBox with better image displaying support. For short: I parse text-based image placeholders from the RTF-input, replace them by an empty paragraph with a propriate spacing (image height) and draw the images in the paint event above the text (inside the spaces).
The problem now is that the spacing seems to be wrong. I calculated the twips with the following formula:
size.Width = (int)((1440 * size.Width) / graphics.DpiX);
size.Height = (int)((1440 * size.Height) / graphics.DpiY);
Where graphics is from my RichTextBox and size is the image size. As the DPI value is 96, it is basically twips = 15 * pixels.
I tried the RTF control words \sbN and \saN with my calculated twips-value (I controlled it with the debugger, the value is as expected). I also used interop with PFM_SPACEBEFORE and PFM_SPACEAFTER.
Both ways give the same result. The problem is that the real space inside the RichTextBox is too big. If I multiply the calculated twips value with 0.75 it fits. But I really don't get why this happens.
My first thought was the factor 72 / 96 (PPI / DPI) which is 0.75. But this makes no sense for me.
The additional space increases proportional to the image height. So the space is barely noteable for small icons (e.g. 24 pixels height). But for larger images (e.g. 320 pixels height) the additional space is huge.
Some ideas? Is this a bug in the RichTextBox control?
To clarify: I used System.Windows.Forms.RichTextBox as a base class.
Ok I found the error by myself. I had to use the DPI values of the image instead of the DPI values of the control. The image DPI values were 120 so the factor was 0.8 (not 0.75). Now it fits. I leave the question here in case someone has the same problem.
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);
}
}
I have a custom WinForms control which could be described as some kind of terminal control (like a control containing Putty). According to this, the control should display a lot of characters, each with a different color and background color (worst case).
Currently I'm using the (kind of obsolete) Graphics.MeasureString method to determine the size of a single character in my fixed-size font so I can calculate the position of a character at a specific row and column. Then I use Graphics.DrawString to draw the characters. To optimize the performance I create a BufferedGraphics and group characters by their properties to draw consecutive characters with the same color with just one DrawString call. (Because one DrawString call per character is really slow.)
Now the problem is that DrawString apparently calculates the width of a character slightly different from MeasureString. When I draw a complete line at once, the resulting text width is different from what I calculated using the width of a single character multiplied by the character count of the line. It's just one or two pixels (or maybe less), but you can see it clearly - especially because I'm using anti-alias so you can even see a difference of just half a pixel.
The following sample code draws a long string a on the form's graphics, followed by character 'B'. Then it draws just a 'B' on the position calculated by measuring a.
var f = new Form {
Width = 1200,
Height = 500,
Font = new Font("Consolas", 11, FontStyle.Regular)
};
f.Paint += delegate(object sender, PaintEventArgs e) {
var a = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
var size = e.Graphics.MeasureString(a, f.Font, new PointF(0, 0), StringFormat.GenericTypographic);
using (var black = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(a + "B", f.Font, black, 0, 0, StringFormat.GenericTypographic);
e.Graphics.DrawString("B", f.Font, black, size.Width, 20, StringFormat.GenericTypographic);
}
};
f.Show();
And if you look closely, you will see that the second B is about one pixel more right - at least with my display settings (100% dpi scale, ClearType enabled). Although one pixel is not much, when you draw lines using unicode characters U+2500 through U+257F it looks pretty ugly if the characters aren't perfectly aligned.
Also I can't use the TextRenderer class because its MeasureString method returns integer values, but DrawString of course does not draw each character on a full pixel position (what would be required to align it with a position calculated using row/column and the measured integer character size).
So my question is: Is there any (efficient) method to draw strings which are perfectly aligned to their corresponding position calculated using the character size?
P.S.: MeasureString with a 200-character-string returns exactly 200 times the return value of MeasureString with a single-character-string.
I have had some similar issues with measuring the strings in Graphics. I have used SizeInPoints, a property from the class Font and multiplied it for the number of characters I have in the string... I dont't know if it helps.
If not it can be a "rounding" problem with the pixels... then I would try to scale up the font size (maybe 10 times), measure it and then divide it by 10 again when using the size to color the background.
I hope it helps.
Good luck! Regards!
I'm having same issue.
It seems that the behavior depends on the font size.
After changing font with following code, this issue didn't occur.
Font = new Font("Consolas", 20.0F, FontStyle.Regular, GraphicsUnit.Pixel)
However, with another font size as below this issue still occurs.
Font = new Font("Consolas", 20.3F, FontStyle.Regular, GraphicsUnit.Pixel)
My guess on background of this issue: MeasureString and DrawString uses different layout routine and they have different characteristic in rounding error of float number. Only with 'simple' font size they gives same results.
Also, this issue didn't occur with bitmap fonts.
In my program I need to generate a bitmap with all digit characters (0..9) laid from left to right, plus a few other characters.
The user will select from the UI:
the desired font, and
the desired character height in pixels.
So I want to create a Bitmap, then a Graphics from this bitmap, then draw the digits one by one to this bitmap, and then save it to disk.
What I couldn't figure out in hours:
How do I create a font with the correct size so that the digit '0' has the height given by the user?
I played with all the parameters in the Font constructor, with properties of StringFormat, with MeasureString/MeasureText/MeasureCharacterRanges, I tried creating the font with the native CreateFont() via P/Invoke (with positive/negative nHeight). All parameters called "size" or "height" seem to indicate the size of some hypothetical character that's much larger than '0'.
I did read the theory with line height, em height, ascent, descent etc. There seem to be no notion for the real height of a character, without padding and spacing and so on
Again my question: Given the font name and the desired pixel height for the '0' glyph, how can I create a font which draws that glyph with the required height?
I would appreciate any guidance.
LATER EDIT
Some more details about my project: It's an embedded device with a big display, and I need to provide an easy way for the designers to to generate and try out bitmap fonts of their liking. A Windows tool that generates such bitmap fonts seemed like a good solution to me.
While I wasn't far off the mark with what I posted here earlier, it didn't actually work. In banging my head against this I found it interesting to note that graphics.MeasureString("M", ...) reports a height far larger than graphics.MeasureString("MM", ...). In calculating the font sizes by hand I've found the first(which correlates with GetEMSize's response) is actually the full line size. In the end I came to realize the actual character sizes of the digits aren't required to correlate to any of the metrics, whether real or .net.
I believe you could use this Font Constructor to specify the GraphicsUnit to be pixels. Then it should create the font with the appropriate size.
Adding test code - edit accordingly for your case and don't judge for style, I just wanted something I could paste in LINQPad and would produce an image.
using (var font = new Font("Arial",10,FontStyle.Regular, GraphicsUnit.Pixel))
using (var image = new Bitmap(30, 15))
using (var graphics = Graphics.FromImage(image))
{
graphics.FillRectangle(Brushes.White, new Rectangle(0, 0, 30, 15));
graphics.DrawString("Ay", font, Brushes.Black, 0, 0);
image.Save(#"E:\test.bmp", ImageFormat.Bmp);
}
Remember when setting size by Pixels that all characters in the Font need to fit in that range, meaning letters with a descender and letters with an ascender.
For example, C # says that the selected image contains 96 ppi, while that same image in Photoshop contains 72 ppi.
Why is there a difference?
I’m inclined to trust Photoshop in this case, and how to test image resolution if C# returns false results?
We need to build some sort of validator control that rejects all images with ppi != 300.
Control should support the following formats: jpg, jpeg, gif, png, bmp.
Code is listed below:
Image i = Image.FromFile(FileName);
Console.Write(i.VerticalResolution);
Console.Write(i.HorizontalResolution);
DPI means dots (pixels) per inch. The physical size in inches is subjective, based on the current monitor's size and resolution. Unless you're relying on metadata (which gif and bmp don't contain) you cannot reliably calculate this.
Photoshop simply has a prescribed value for DPI, which it uses when translating images for print. This value is stored in the PSD file and may be copied to JPEG metadata, but if you save the image in a format without DPI metadata, the information is not stored.
Update:
The reason your code gets a different value is that C# fetches its VerticalResolution and HorizontalResolution values from the current DPI setting on the computer. Photoshop's DPI is for use with print, so it knows the physical dimensions if you want to send your image to a printer. It has a default value of 72dpi, but you can change this. The value has no meaning on a screen, though, since screens deal in pixels only.
DPI means dots per inch. A bitmap image does not have an inherent DPI, it merely has a size which is the number of pixels in the horizontal and the number of pixels in the vertical (width and height). An image only gains a resolution (in DPI) when you say how many pixels you want to squeeze into each inch.
So if I have an image that is 100 pixels wide and 100 pixels high (100px × 100px), it will be 100 DPI if I print it (or convert it into a format that dictates the print size) such that it fits exactly in a one square inch (1" × 1"). It will be 50 DPI if I print it to fit in a square that is two inches by two inches, &c.