This is one of the strange behavior of csharp code under my testing.
I use DrawString method to draw some unicode character under Times New Roman Font. However, I can draw some characters and but some characters are just drawn as blank square (i.e. unsupported character.
To elaborate more, I can draw 42 (*) and 43 (+) without any problem using DrawString method.
But I can not draw 9978 and 9900. Both 9978 and 9900 are the supported characters by Times New Roman. Check the font map here.
https://www.martinstoeckli.ch/fontmap/fontmap.html
For your information, when I try to print the same characters in the text box window control, everything works fine. Textbox show all the supported characters including 9978 and 9900.
However, just this DrawString method is inconsistent under my testing. Why I can not draw these supported characters under the font using DrawString method ?
int unicodeIndex = 10728;
this._provider = new CultureInfo("en-us");
string unicodeString = ((char)unicodeIndex).ToString(_provider);
this.Font = new Font("Times New Roman", 10);
int x = 200;
int y = 200;
SizeF sizef = g.MeasureString(unicodeString+"W", this.Font, new PointF(x, y), _sformat);
int w = Convert.ToInt32(sizef.Width) + 2;
int h = Convert.ToInt32(sizef.Height) + 2;
RectangleF rectf = new RectangleF(x, y-h/2, w, h);
g.DrawString(unicodeString, this.Font, myBrush, rectf, myFormat);
Some more clue here as this seems tough problem.
I am currently using East Asian Language pack installed on my Window.
Is there any possibility that East Asian Language pack installed on my window can cause this problem ? For example, in localized window with Chinese and Japanese language, "Times New Roman" font might be not called correctly ? Just guess though.
I changed your code a bit and It's working well now:
private void Form1_Paint(object sender, PaintEventArgs g)
{
CultureInfo _provider = new CultureInfo("en-us");
String drawString = ((char)9900).ToString(_provider);
Font drawFont = new Font("Times New Roman", 16);
SolidBrush drawBrush = new SolidBrush(Color.Black);
float x = 150.0F;
float y = 50.0F;
StringFormat drawFormat = new StringFormat();
g.Graphics.DrawString(drawString, drawFont, drawBrush, x, y, drawFormat);
}
Times New Roman (any version, I'm quite sure) actually does not have the characters in question.
The second sentence on https://www.martinstoeckli.ch/fontmap/fontmap.html says:
What you see on this page is all declared as font Arial, though the operating system and the browser can substitute missing characters with characters from other fonts.
What you're seeing on that webpage is font substitution.
Related
Preface: We sell machines that tell you the quality of the air. We have a touch screen display that we recently upgraded to support Chinese. I wrote a C# application that connects to the device serially and attempts to draw the screen so users can connect to their device remotely and control it. This works great until you put the device into Chinese, then my C# app draws all the symbols as their ASCII equivalent (not Chinese symbols).
I use the Graphics library to do all my drawing, in which I call the DrawString method to draw text on the screen. I have a picturebox in which I am doing the graphics drawing on. How do I draw the Chinese correctly?
Code:
private Bitmap image;
private Color gForeColor;
private Color gBackColor;
private Font gFont;
private int gTextsize;
private int gLineSize;
private StringFormat gTextAlign;
private void Initialize()
{
gForeColor = Color.Black;
gTextsize = 14;
gLineSize = 1;
gFont = new Font("Microsoft Sans Serif", gTextsize, FontStyle.Regular);
gTextAlign = new StringFormat();
gTextAlign.LineAlignment = StringAlignment.Center;
gTextAlign.Alignment = StringAlignment.Near;
gTextAlign.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
image = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}
private void DrawText(string Text, int X1, int Y1)
{
using (Graphics g = Graphics.FromImage(image))
{
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
SolidBrush sb = new SolidBrush(gForeColor); //Foreground
SolidBrush bb = new SolidBrush(gBackColor); //Background
g.DrawString(Text, gFont, sb, X1, Y1, gTextAlign);
pictureBox1.Invalidate(); //refresh
gTextAlign.Alignment = StringAlignment.Near; //horizontal alignment reverts to Left after a text display command is issued
}
}
I tried adding this line
Text = Encoding.UTF8.GetString(Encoding.Default.GetBytes(Text)); To try and decode the text properly but now it prints '??????' instead of the ASCII letters or Chinese symbols.
Example:
English
Chinese
Edit: It seems the ReadExisting() from the SerialPort was converting all the bytes into '??????'. I stopped using ReadExisting and switched to Read which keeps the raw bytes. So now I'm getting the correct ASCII, but still not Chinese. I re-added my line Text = Encoding.UTF8.GetString(Encoding.Default.GetBytes(Text)); and now I'm getting Chinese with some extra symbols that aren't correct. Am I using the wrong encoding?
Are you sure, that you can use Microsoft Sans Serif with Chinese? I highly doubt.
When I try to render a Chinese string like 试标记好不好 Graphics.DrawString draws it
even if I change the Font Linking to SimSun. On the other hand TextRenderer works but it fails to render readable strings when bold fonts are used. It seems there is no correct way to render bold strings.
The issue is described in greater detail here. Am I doing something wrong or does Windows not support professional looking localizable applications with some bold strings in the UI?
The code to repro the issue is for a Windows Forms application with two PictureBoxes:
const string combined = "测试标记好不好This is a aber long";
private void cFontSize_ValueChanged(object sender, EventArgs e)
{
Bitmap bmp = new Bitmap(750, 140);
Font f = new Font("Arial", (float)cFontSize.Value, IsBold ? FontStyle.Bold : FontStyle.Regular);
using (var g = Graphics.FromImage(bmp))
{
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
// Rendering with a Background Color solves the issue but
// this would produce boxes of white or black color in the displayed image which looks even worse
TextRenderer.DrawText(g, combined, f, new Point(0, 0), FontColor);
}
cPictureBox.Image = bmp;
Bitmap bmp2 = new Bitmap(750, 140);
using (var g = Graphics.FromImage(bmp2))
{
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
g.DrawString(combined, f, FontBrush, 0, 0);
}
cPicture2.Image = bmp2;
}
Update 1:
When I add as Font Link Setting to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink
Arial Bold
SIMSUNB.TTC,SimSun Bold
MSGOTHIC.TTC,MS UI Gothic
then Graphics.DrawString looks ok now althogh TextRender now gets problems. After restarting the application now both outputs look font wise ok although TextRenderer still has the problem that bold fonts become unreadable due to anti aliasing with black. I will restart the machine to check out any caching effects.
I have a form which reads the text from a textbox and then draws it with the DrawString method from System.Drawing.Graphics.
Depending on which overload I use, the result is different. The overload that expects a additional StringFormat seems to ignore tabs. The overload without StringFormat works just fine.
The result looks like this:
This is the code:
public Form1()
{
InitializeComponent();
textBox1.AcceptsTab = true;
textBox1.Multiline = true;
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Graphics g = this.CreateGraphics();
Font font = new Font("Arial", 20);
StringFormat format = StringFormat.GenericDefault;
format.Trimming = StringTrimming.None;
g.DrawLine(new Pen(Color.Black), new Point(10, 50), new Point(10, 140));
g.DrawString(textBox1.Text, font, Brushes.Black, new Point(10,60));
g.DrawString(textBox1.Text, font, Brushes.Black, new Point(10,100), format);
}
}
It seems that I could set the TabStops manually by calling format.SetTabStops(), but thats not a preferred solution.
I tried to set format.Trimming = StringTrimming.None but that doesn`t work.
Is there a possibility to print the tabs from the text while using the StringFormat overload?
Yeah, it looks like design fallacy of Graphics.DrawString — with having less-detailed overload and more-detailed overload, e.g.:
DrawString(String, Font, Brush, PointF)
DrawString(String, Font, Brush, PointF, StringFormat)
— it doesn't allow the more-detailed overload to be called in a way that fully resembles the behavior of the less-detailed overload.
For example:
calling graphics.DrawString(…, new StringFormat()) will make all tabs zero-width;
calling var stringFormat = new StringFormat(); stringFormat.SetTabStops(0, new float[] { 8 }); graphics.DrawString(…, stringFormat) will make all tabs to have width of 8 pixels, not 8 spaces;
of course, you can mangle something like var stringFormat = new StringFormat(); stringFormat.SetTabStops(0, new float[] { Font.SizeInPoints / 2 * e.Graphics.DpiX / 72 * 8 }); graphics.DrawString(…, stringFormat) — de facto it works (the specified tab-width coincides with the tab-width of StringFormat-less DrawString-call; last item in the float[] array is applied to all further tabs) — but no parts of documentation guarantee that it won't change in future.
Of course, technically calling a StringFormat-less DrawString overload is equivalent to passing null into StringFormat parameter — but that doesn't help in practice (e.g. if you want to specify some of StringFormat properties, but keep default tab-behavior).
I am working on a small project for a Retail Management Software, which will be using a POS printer (I think that's what we call it). I need a create a bill for it in the end. But i am stuck here, and not able to proceed. So suppose if I generate my bill in a separate form with appropriate dimensions (of width of POS bills), will i be able to print it properly?
I am using C# and .NET 4.0 framework. I don't have much knowledge about POS devices. I am working for really a small local client which needs a basic model of software. I am also a fresher so please help me out.
If my question is not clear, let me know i will try to elaborate my thought.
I know this is an old post, but for those still looking for a solution, I can tell you what I did.
After spending many hours messing with OPOS and POS for .Net, I ended up just abandoning those and just using the built-in System.Drawing.Printing libraries. The OPOS and POS for .Net ended up being a pain to get working and ultimately didn't work as well as the built-in libraries.
I'm using an Epson TM-T20II receipt printer.
Here's some code that worked well for me.
public static void PrintReceiptForTransaction()
{
PrintDocument recordDoc = new PrintDocument();
recordDoc.DocumentName = "Customer Receipt";
recordDoc.PrintPage += new PrintPageEventHandler(ReceiptPrinter.PrintReceiptPage); // function below
recordDoc.PrintController = new StandardPrintController(); // hides status dialog popup
// Comment if debugging
PrinterSettings ps = new PrinterSettings();
ps.PrinterName = "EPSON TM-T20II Receipt";
recordDoc.PrinterSettings = ps;
recordDoc.Print();
// --------------------------------------
// Uncomment if debugging - shows dialog instead
//PrintPreviewDialog printPrvDlg = new PrintPreviewDialog();
//printPrvDlg.Document = recordDoc;
//printPrvDlg.Width = 1200;
//printPrvDlg.Height = 800;
//printPrvDlg.ShowDialog();
// --------------------------------------
recordDoc.Dispose();
}
private static void PrintReceiptPage(object sender, PrintPageEventArgs e)
{
float x = 10;
float y = 5;
float width = 270.0F; // max width I found through trial and error
float height = 0F;
Font drawFontArial12Bold = new Font("Arial", 12, FontStyle.Bold);
Font drawFontArial10Regular = new Font("Arial", 10, FontStyle.Regular);
SolidBrush drawBrush = new SolidBrush(Color.Black);
// Set format of string.
StringFormat drawFormatCenter = new StringFormat();
drawFormatCenter.Alignment = StringAlignment.Center;
StringFormat drawFormatLeft = new StringFormat();
drawFormatLeft.Alignment = StringAlignment.Near;
StringFormat drawFormatRight = new StringFormat();
drawFormatRight.Alignment = StringAlignment.Far;
// Draw string to screen.
string text = "Company Name";
e.Graphics.DrawString(text, drawFontArial12Bold, drawBrush, new RectangleF(x, y, width, height), drawFormatCenter);
y += e.Graphics.MeasureString(text, drawFontArial12Bold).Height;
text = "Address";
e.Graphics.DrawString(text, drawFontArial10Regular, drawBrush, new RectangleF(x, y, width, height), drawFormatCenter);
y += e.Graphics.MeasureString(text, drawFontArial10Regular).Height;
// ... and so on
}
Hopefully it helps someone skip all the messing around with custom drivers. :)
Well I designed a POS application (in Delphi) and altough there are many little issues with printing a receipt or a bill it´s not rocket science. I just print a receipt by simply, well, printing like any other printer, the difference is that you send lines of 30-38 characters long (depending on the printer driver, font and size). To start, you could print using 2 methods: Sending Ascii characters and sending printer commands (to set the font style, color, etc) for that specific printer or the second method is to set the font, size, etc using C# like printing to any other normal/desktop printer.
You could try printing following the example of the page and my suggestions:
http://ondotnet.com/pub/a/dotnet/2002/06/24/printing.html
I am using Graphics.DrawString to draw my usercontrol's text like this:
protected override void OnPaint(PaintEventArgs e)
{
RectangleF bounds = DisplayRectangle;
bounds.Inflate(-4, -4); // Padding
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Near;
format.LineAlignment = StringAlignment.Near;
format.Trimming = StringTrimming.None;
using (Brush bFore = new SolidBrush(ForeColor))
{
g.DrawString(Text, Font, bFore, bounds, format);
}
}
If control's Text is wider than the DisplayRectangle, DrawString nicely breaks the Text into multiple lines at word boundaries.
Now I want to underline some words from Text, but I couldn't work it out. I tried splitting the Text, then MeasureString the string just before an underlined part starts, DrawString the normal part, then DrawString the underlined part. But this works only if Text is single-line.
I am sure using a child LinkLabel or RichTextBox to render my control's text will solve this, but I don't like the idea of using a child control just to underline a few words. Is there another way?
This is a crude example that will work using the string split into parts and two different font styles, rather than drawing the underline separately ( though that would work too ). In actual practice, I would recommend splitting the text by word, not by phrase, and dealing with each word individually, in a loop. Otherwise, like in this example, the line-breaking doesn't work quite right.
Dim fntNormal As New Font(myFontFamily, myFontSize, FontStyle.Regular, GraphicsUnit.Pixel)
Dim fntUnderline As New Font(myFontFamily, myFontSize, FontStyle.Underline, GraphicsUnit.Pixel)
g.DrawString("This is ", fntNormal, Brushes.Black, rctTextArea)
w1 = g.MeasureString("This is ", fntNormal).Width
w2 = g.MeasureString("underlined", fntUnderline).Width
If w1 + w2 > rctTextArea.Width Then
yPos = rctTextArea.Y + g.MeasureString("This is ", fntNormal).Height + 5
xPos = rctTextArea.X
Else
yPos = rctTextArea.Y
xPos = 0
End If
g.DrawString("underlined", fntUnderline, Brushes.Black, xPos, yPos)
w1 = g.MeasureString("underlined", fntUnderline).Width
w2 = g.MeasureString(", and this is not.", fntNormal).Width
If w1 + w2 > rctTextArea.Width Then
yPos += g.MeasureString("underlined", fntUnderline).Height + 5
xPos = rctTextArea.X
Else
xPos = 0
End If
g.DrawString(", and this is not.", fntNormal, Brushes.Black, xPos, yPos)
This code can really be cleaned up and made more efficient to let you loop through each word in your text string.
This example also does not include any code to check if you have exceeded the vertical limit of your bounds rectangle.
Sorry for the VB code, I just noticed your question was in C#.