This is for a header/footer on a printed page. The code below is painting the left & center parts of the header/footer (I've omitted the code that paints the right) and simplified logic to illustrate the problem better. The idea is the center part of header takes precedence and the left and right will be trimmed via StringTrimming.EllipsisPath
if the center is too big for them to fit. This all works except for whenever trimming occurs, GDI+ is shifting the trimmed text vertically a bit.
EDIT: I've written a dedicated sample to illustrate the problem because my original post was not clear enough for folks. This is the entire code of the sample. It's a .NET Framework Winforms app created with VS19.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
Font font = new Font("Century Gothic", 24F, FontStyle.Regular, GraphicsUnit.Point);
string demo = "[Emergency 123]";
StringFormat fmtTrimmed = new StringFormat(StringFormat.GenericTypographic)
{
Alignment = StringAlignment.Near,
LineAlignment = StringAlignment.Near,
Trimming = StringTrimming.EllipsisCharacter,
};
StringFormat fmtNotTrimmed = new StringFormat(StringFormat.GenericTypographic)
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Near,
};
try
{
Rectangle boundsHeader = new Rectangle(10, 10, this.ClientSize.Width - 20, (int)font.GetHeight() + 8);
SizeF textSize = e.Graphics.MeasureString(demo, font, boundsHeader.Size, fmtNotTrimmed);
boundsHeader.Inflate(1, 1);
e.Graphics.DrawRectangle(Pens.Blue, boundsHeader);
float textCenterBounds = (boundsHeader.Width - textSize.Width) / 2;
e.Graphics.DrawRectangle(Pens.Green, boundsHeader.X + textCenterBounds, boundsHeader.Y, textSize.Width, textSize.Height);
e.Graphics.DrawString(demo, font, Brushes.Black, boundsHeader, fmtNotTrimmed);
RectangleF boundsLeft = new RectangleF(boundsHeader.X, boundsHeader.Y, textCenterBounds, boundsHeader.Height);
SizeF sizeLeft = e.Graphics.MeasureString(demo, font, boundsLeft.Size, fmtTrimmed);
e.Graphics.DrawRectangle(Pens.Red, boundsLeft.X, boundsLeft.Y, boundsLeft.Width, textSize.Height);
e.Graphics.DrawString(demo, font, Brushes.Black, boundsLeft, fmtTrimmed);
}
finally
{
font.Dispose();
fmtTrimmed.Dispose();
fmtNotTrimmed.Dispose();
}
}
Here the app shows that if left
is of size that trimming does not occur, it is aligned vertically correctly:
If left
is has a length where trimming occurs (note elipsis), vertical alignment shifts up slightly (note code uses Form1_Resize
to resize rects).
You can see this most clearly by looking at the y
, g
, and brackets.
I've tried every combination of StringTrimming
, LineAlignment
, and FormatFlags
and cannot resolve this. Note that in the current implementation I've also tried making the Rectangle have a height of 0. Same issue occurs if the bounds.Height
is the font height, sizeCenter.Height
, or bigger.
I can't believe this is a bug in GDI+, so I've got to be doing something wrong. Many searches and re-reads of the docs have not helped. I'm at my wits end.
EDIT: based on @HansPassant's comments below, I've found different fonts behave differently. In the images above I was using "Microsoft Sans Serif".
- "Arial" has same problem (bold or regular)
- "Tahoma" does reproduce the problem.
- "Cascadia Code" DOES reproduce (it's new and fixed pitch)
- "Lucida Console" DOES NOT!?!?! reproduce
- "Courier New" DOES.
I've determined that some fonts are worse than others. For example, if I change the code above to use 24F
as the font size I can't reproduce it with Microsoft Sans Serif
).
I've also determined that StringTrimming.Character
and StringTrimming.ElipsisCharacter
and StringTrimming.ElipsisPath
all behave the same way (so the ellipsis char does not seem to be the cause).
EDIT: Showing another example here using Century Gothic
regular:
EDIT: This sample illustrates that e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias
does NOT fix the problem when LineAlignment = StringAlignment.Far
.