I am trying to estimate the length of a printed string.
Font newFont = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Point);
label1.Font = newFont;
labe1.Text = "300028";
Graphics g = Graphics.FromHwnd(label1.Handle);
SizeF txtSize = g.MeasureString(label1.Text, label1.Font);
txtSize is {Width=60.3177, Height=19.875} points.
The actual width should be 60.3177 * 0.353 = 21.29 mm
where (1 point = 1/72 inch = 0.353 mm)
On paper (printed with Word) the width is about 13.5 mm
Why do we get such a big difference between the value computed with MeasureString (21.29 mm) and the real one (13.5 mm)?
I am aware of the limitations of the MeasureString method but I do not think this cannot justify such a big difference.
What I am missing?
Because you initialize your Graphics object wrong. You are using a display handle, not a print handle.
According to this post your Graphics object should be obtained using the PrinterSettings.CreateMeasurementGraphics method on a PrintDocument:
Graphics g = pd.PrinterSettings.CreateMeasurementGraphics();
Printing units are by default in hundredths of an inch, not 72ths of an inch.
As the other answer mentions, you need to use PrinterSettings.CreateMeasurementGraphics to get a graphics object that will be configured the right way to measure text for printing.
Related
I'm drawing a GraphicPant with lines that are intersecting and I'm wondering if the edges of the lines could be rounded.
The code with that I draw is:
Graphics G = e.Graphics;
GraphicsPath gp = new GraphicsPath();
gp.AddLine((float)(line.startX), (float)(line.startY), (float)(line.endX), (float)(line.endY));
gp.CloseFigure();
using (Pen pen = new Pen(Color.DarkGray, 0.0001f))
{
G.SmoothingMode = SmoothingMode.AntiAlias;
G.Clear(Color.White);
G.DrawPath(pen, gp)
}
If it could look like this:
Try:
pen.StartCap = LineCap.Round;
pen.EndCap = LineCap.Round;
Using such a tiny Pen.Width will result in a line that is only one pixel 'thick'. But pixels are always square by definition. So if you enlarge with a non-dithering or -antialising software it will look as if the ends were square.
But they really have no shape at all as they do not have a real size. Instead their width is a virtual number: The one pixel that is used is simply the default minimum used so the line doesn't disappear.
So: Yes the Pen.Width does matter.
So: Do set it to a reasonable number greater than 1 and you will see the round endpoints..
You could also scale the Graphics object by a suitable number and you would see the rounded ends as well..If you want to try that, don't forget to adapt the coodinates to the extreme scaling!
For several lines created by AddLines (or AddPolygon if you were serious about the CloseFigure) also set the LineJoin:
pen.LineJoin = LineJoin.Round;
pen.EndCap = LineCap.Round;
pen.StartCap = LineCap.Round;
I have a great question for you. I searched all the Google and MSDN and didn't find anything.
I'm trying to do a program that exports a font to single PNG images per character. I'm currently testing it with new Windows font, Segoe UI Symbol. Please note I know the font license terms and I won't distribute that font in the internet.
Well, the real problem is happening when I call the method DrawString, member of Graphics. I convert the unicode integer value to a char then to a string. I already tried to convert the integer to char with char.ConvertFromUtf32() and with Convert.ToChar().
The program is working good during 26 characters (starting from 57344 = 0xE000), the problem doesn't appear when I use numeric values until 57370. After this, there is not a single number that is not written with a white box char.
After some search, I found an overload to Font constructor, with the attribute gdiCharset and tried to use its value as 2, but nothing was happened.
I'm showing the source code for you below. Please, if there is someone who can help me, I will be happy.
UPDATE
When I use escape sequences (like "\uE1FF" instead of char conversion it works! But I don't know how to make escape sequences within a for loop.
Font segoe = new Font("Segoe UI Symbol", 800, FontStyle.Regular, GraphicsUnit.Pixel, 2);
Bitmap bmedidor = new Bitmap(1000, 1000, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics gmedidor = Graphics.FromImage(bmedidor);
// This line below doesn't matter, see the method DrawString
Size tamanho = gmedidor.MeasureString(char.ConvertFromUtf32(57344).ToString(), segoe).ToSize();
int[] reducoes = new int[6] {512, 256, 128, 64, 32, 16};
string caminho = "C:\\InoMetro";
for (int u = 57344; u < 57896; u++)
{
Bitmap caractere = new Bitmap(tamanho.Width, tamanho.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics criador = Graphics.FromImage(caractere);
criador.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
criador.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
criador.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
criador.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
// Here we have the problem
criador.DrawString(Convert.ToChar(u).ToString(), segoe, new SolidBrush(Color.Black), new PointF(0, 0));
for (int r = 0; r < reducoes.Length; r++)
{
int taR = reducoes[r];
Bitmap reducao = new Bitmap(taR, taR, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics redutor = Graphics.FromImage(reducao);
redutor.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
redutor.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
redutor.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
redutor.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
redutor.DrawImage(caractere, 0, 0, taR, taR);
reducao.Save(caminho + "\\" + taR.ToString() + "\\" + u.ToString() + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
}
U+E000-U+F8FF (57344-63743 decimal) are Private Use Area characters.
Most fonts (including Segoe UI Symbol) don't provide glyphs for any code points in this range, so the typical fallback behavior is for the font renderer to display a white box () or a question mark in a black diamond (�) when asked to draw one of these code points.
Segoe UI Symbol on Windows 8 only defines glyphs for U+E000-U+E019, then provides no glyphs for U+E01A-U+E051; that's why white boxes are displayed for 57370 (0xE01A) through 57425 (0xE051).
Is there any way to calculate text width based on available height in c# on windows forms?
Edit: I have the font size. I want to calculate the minimum width required for drawing the text considering the line could be wrapped.
what you can do is to measure a string in a default size with this method:
(g is a Graphics Object)
g.MeasureString("area", Font, maxWidth)
you scale the fontsize depending on the proportion measuredHeight to availableHeight. After you can remeasure the string with the height of the available area
or you just measure it to get the proportions and calculated the expected width like that:
float measureFontSize = 5;
SizeF measuredBox = g.MeasureString("my string", new Font("Arial", measureFontSize));
double measuredProportion = measuredBox.Width / measuredBox.Height;
double expectedWidth = measuredProportion * wishedHeight;
I have an requirement that asks for an image with 10 X 6,88 cm.
I know that I can't simple convert from cm to pixels, cause one pixel size depends on the user display resolution.
I would like to know if there is a way to resize an image to have that size in cm. (I need to keep the image extension also. e.g.: can't convert it to a pdf or other extension)
It really depends on in which resolution the user will print the image (sizes in cm makes little sense other than when printed). If the user wants to make a print in, say 200 dpi, then the image would need to be (10 / 2.54 * 200) by (6.88 / 2.54 * 200) pixels (the division with 2.54 is needed to convert between cm and inches). Which resolution that is needed is highly dependent on what kind of image it is, and the quality requirements of the user.
So just saying "I want to resize to X by Y cm" does not really make sense.
For a code sample on how to make the actual resize once you have figured out the needed size of the image, this SO answer should cover your needs.
Actually, you have to differentiate between the images size on the screen, and the images size on the printout.
usually, you find the formula:
inches = pixels / dpi
so it follows:
pixel = inches * dpi
This is for print, actually.
For the display, replace dpi with ppi, and there you are.
For those (like me) that are not familiar with inches:
inches = pixels / dpi
pixel = inches * dpi
1 centimeter = 0.393700787 inch
pixel = cm * 0.393700787 * dpi
This routine will calculate the pixel-size to have the image display X-cm on the monitor.
But on the printer, you don't have it that easy, since you can't get the DPI as easy as the PPI (bmp.HorizontalResolution & bmp.VerticalResolution).
public static int Cm2Pixel(double WidthInCm)
{
double HeightInCm = WidthInCm;
return Cm2Pixel(WidthInCm, HeightInCm).Width;
} // End Function Cm2Pixel
public static System.Drawing.Size Cm2Pixel(double WidthInCm, double HeightInCm)
{
float sngWidth = (float)WidthInCm; //cm
float sngHeight = (float)HeightInCm; //cm
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(1, 1))
{
sngWidth *= 0.393700787f * bmp.HorizontalResolution; // x-Axis pixel
sngHeight *= 0.393700787f * bmp.VerticalResolution; // y-Axis pixel
}
return new System.Drawing.Size((int)sngWidth, (int)sngHeight);
} // End Function Cm2Pixel
usage would go like this:
public System.Drawing.Image Generate(string Text, int CodeSize)
{
int minSize = Cm2Pixel(2.5); // 100;
if (CodeSize < minSize)
CodeSize = minSize;
if (string.IsNullOrEmpty(Text))
{
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(CodeSize, CodeSize);
using (System.Drawing.Graphics gfx = System.Drawing.Graphics.FromImage(bmp))
{
gfx.Clear(System.Drawing.Color.Black);
using(System.Drawing.Font fnt = new System.Drawing.Font("Verdana", 12, System.Drawing.FontStyle.Bold))
{
double y = CodeSize / 2.0 - fnt.Size;
gfx.DrawString("No Data", fnt, System.Drawing.Brushes.White, 5, (int)y, System.Drawing.StringFormat.GenericTypographic);
} // End Using fnt
} // End using gfx
return bmp;
} // End if (string.IsNullOrEmpty(Text))
...[Generate QR-Code]
return [Generated QR-Code]
}
Image file formats like JPG and TIFF have an EXIF header which has information like horizontal and vertical DPI.
Thus if you get an image that has this metadata, you could verify the printable size.
double DPC = Image_DPI * 0.393700787;
double widthInCm = Image_Width * DPC;
double heightInCm = Image_Height * DPC;
if (widthInCm <= 10 && heightInCm <= 6.88) // do stuff
If you need to resize images to never exceed these printable dimensions, you could do it the other way around, and calculate a DPI ratio that lets the image of dimensions W x H fit within 10cm x 6.88cm bounds.
Kind of what Fredrik is saying:
I would take a nice DPI and require the image to be that resolution or bigger (but is the same aspect ratio) and when exporting/printing the image, resize the image to the DPI used by the other program/printer...
It might be as simple as this: most images store the number of pixels per inch in them. Figure out the number of pixels in each dimension of your image, and divide that by the number of inches (convert from cm). Then use the original bits, just modify the field for the number of pixels per inch (or, more commonly, dots per inch).
So your picture needs to be 3.93" x 2.71". If your image is 393px x 271px, you would set the dpi to 100x100. If your image is 39px x 27px, you would set the dpi to 10x10.
Though probably you'll have to do some resizing, as explained by other answers. :)
From what I've understood the relationship point to pixel will depend on the screen resolution. So how can I calculate it at run-time in c#?
Thanks
If you're trying to get the DPI of the screen it's a bit trickier. You'll have to create a real Graphics object and query that.
For example, in the Load event of your main form:
using( Graphics g = CreateGraphics() )
{
_dpiX = g.DpiX;
_dpiY = g.DpiY; // In practice usually == dpiX
_points = _dpiX / 72.0f; // There are 72 points per inch
}
Of course most monitors lie about the actual DPI and always return 72 or 96, or when large fonts are enabled 120. If you actually want to map a physical inch to a the screen you'll have to actually calibrate it with the user's help - having them pick a line that they measure to be 1 inch.
It is all in the Screen object:
int bpp = System.Windows.Forms.Screen.PrimaryScreen.BitsPerPixel;
int wid = Screen.PrimaryScreen.WorkingArea.Width;
int ht = Screen.PrimaryScreen.WorkingArea.Height;
On my machine it gives:
bpp=32
width=1280
height=740