I needed to write text left-to-right but each character rotated 90 degrees counter clock wise so that on a print out it would look like the text is written vertically top-to-bottom like this:

To achieve this I've created a temporary Bitmap with the same resolution, PageUnit and PageScale properties as the main canvas this needs to be drawn on. I draw the string onto the Bitmap and position the characters as required and then DrawImage the Bitmap onto the main canvas.
This works correctly except that adjusting the main canvas' PageScale causes the Bitmap's text to pixelate even though its PageScale gets adjusted as well before the DrawString happens. Drawing the same string directly onto the main canvas (not rotated, of course) produces a perfect result. The text scales smoothly withe the PageScale.
Can anything think of a potential reason for this?
private void DrawStringVerticalStackingV4(string text, Graphics g, SizeF sizeMaxArea, PointF startPoint, Font font, Brush brush, StringFormat sf)
{
if (text.Length == 0) return;
// Duplicate the StringFormat but change the alignment and directionality.
StringFormat sfVertical = (StringFormat)sf.Clone();
sfVertical.Alignment = StringAlignment.Near;
sfVertical.LineAlignment = StringAlignment.Near;
sfVertical.FormatFlags = StringFormatFlags.DirectionVertical;
SizeF sizeFullText = g.MeasureString(text, font);
// Measure all the characters
SizeF[] sizeChars = new SizeF[text.Length];
for (int i = 0; i < text.Length; i++)
{
sizeChars[i] = g.MeasureString(text[i].ToString(), font, sizeMaxArea, StringFormat.GenericTypographic);
}
// Create bitmap to draw the string on
float sumCharsHeight = (from item in sizeChars select item.Height).Sum();
float maxCharsWidth = (from item in sizeChars select item.Width).Max() * 1.25f;
Bitmap bmp = new Bitmap(MillimeterToPixel(g, sumCharsHeight), MillimeterToPixel(g, sizeMaxArea.Height), g);
Graphics gBmp = Graphics.FromImage(bmp);
bmp.MakeTransparent();
gBmp.PageUnit = g.PageUnit;
gBmp.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;
// Draw each character at a time in reverse order
float currentX = -1.5f;
for (int i = text.Length - 1; i >= 0; i--)
{
string currentChar = text[i].ToString();
gBmp.DrawString(currentChar, font, brush, currentX, (sizeMaxArea.Height / 2) - (sizeChars[i].Width / 2), sfVertical);
currentX += sizeChars[i].Height;
}
// Flip the bitmap
bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
// Crop the temporary bitmap to the maximum size
RectangleF srcRect = new RectangleF(0, 0, Math.Min(gBmp.VisibleClipBounds.Width, sizeMaxArea.Width), Math.Min(gBmp.VisibleClipBounds.Height, sizeMaxArea.Height));
Bitmap bmpCropped = bmp.Clone(new Rectangle(0, 0, MillimeterToPixel(gBmp, srcRect.Width), MillimeterToPixel(gBmp, srcRect.Height)) , bmp.PixelFormat);
Graphics gCropped = Graphics.FromImage(bmpCropped);
gCropped.PageUnit = g.PageUnit;
// Draw the bitmap on the original canvas
srcRect.Width = gCropped.VisibleClipBounds.Width;
srcRect.Height = gCropped.VisibleClipBounds.Height;
RectangleF destRect = new RectangleF(startPoint.X, startPoint.Y, gCropped.VisibleClipBounds.Width, gCropped.VisibleClipBounds.Height);
// This is important!!! Without it, the copied image is distorted.
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.DrawImage(bmpCropped, destRect, srcRect, g.PageUnit);
// Housekeeping
gCropped.Dispose();
bmpCropped.Dispose();
gBmp.Dispose();
bmp.Dispose();
}
More Information
I have observed that it was the font size that wasn't changing correctly when drawing on the Bitmap so I did a few test that surprised me and I still don't quite understand why it's working the way it is.
Consider an empty form with just a PictureBox and a TrackBar going from 1 to 19 and set to 10 by default. The TrackBar controls the scale variable and it refreshes the PictureBox when its moved.
private void trackBar1_Scroll(object sender, EventArgs e)
{
scale = trackBar1.Value / 10f;
pictureBox1.Refresh();
}
Scenario 1 PageScale isn't changed, but the scale controls the font size. In this scenario, MeasureString returns the expected result whereby it returns a different size when the font size changes and the drawn test change in size with the font size.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.PageUnit = GraphicsUnit.Millimeter;
Console.WriteLine("Scale: " + scale);
Font fontMM = new Font("Calibri", 8 * scale);
Console.WriteLine("Font: " + fontMM.Size + " Millimeters: " + g.MeasureString("Test 123", fontMM));
g.DrawString("Test 123", fontMM, new SolidBrush(Color.Black), 10, 10);
}
Returns:
Scale: 1
Font: 8 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.1
Font: 8.8 Millimeters: {Width=12.09137, Height=4.17766}
Scale: 1.2
Font: 9.6 Millimeters: {Width=13.19058, Height=4.557447}
Scale: 1.3
Font: 10.4 Millimeters: {Width=14.28979, Height=4.937234}
Scale: 1.4
Font: 11.2 Millimeters: {Width=15.38901, Height=5.317022}
Scale: 1.5
Font: 12 Millimeters: {Width=16.48823, Height=5.696809}
Scale: 1.6
Font: 12.8 Millimeters: {Width=17.58744, Height=6.076596}
Scale: 1.7
Font: 13.6 Millimeters: {Width=18.68666, Height=6.456384}
Scenario 2 Only PageScale changes but font size stays the same. As expected, MeasureString returns a reducing size as the scale grows because the text would get smaller since the font stayed the same. BUT - DrawString draws the exact same size string every time regardless of PageScale if the font size is the same!!!
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.PageScale = scale;
Console.WriteLine("Scale: " + scale);
g.PageUnit = GraphicsUnit.Millimeter;
Font fontMM = new Font("Calibri", 8);
Console.WriteLine("Font: " + fontMM.Size + " Millimeters: " + g.MeasureString("Test 123", fontMM));
g.DrawString("Test 123", fontMM, new SolidBrush(Color.Black), 10, 10);
}
This returns:
Scale: 1
Font: 8 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.1
Font: 8 Millimeters: {Width=9.992863, Height=3.452611}
Scale: 1.2
Font: 8 Millimeters: {Width=9.160125, Height=3.164894}
Scale: 1.3
Font: 8 Millimeters: {Width=8.455501, Height=2.921441}
Scale: 1.4
Font: 8 Millimeters: {Width=7.851536, Height=2.712766}
Scale: 1.5
Font: 8 Millimeters: {Width=7.3281, Height=2.531915}
Scale: 1.6
Font: 8 Millimeters: {Width=6.870094, Height=2.37367}
Scale: 1.7
Font: 8 Millimeters: {Width=6.465971, Height=2.234043}
Scale: 1.8
Font: 8 Millimeters: {Width=6.10675, Height=2.109929}
Scale: 1.9
Font: 8 Millimeters: {Width=5.785342, Height=1.99888}
Scenario 3 Since the font size on the screen did not actually change as PageScale increased, the only thing left to do is to increase the font size as well and this is where the problem lies. Increasing PageScale and the font size by the same factor effectively negates each other as far as MeasureString seems to be concerned! This means that MesaureString returns the exact same size no matter what the font size is. The positive thing though is that the drawn string does actually increase in size as one would expect when increasing PageScale.
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.PageScale = scale;
Console.WriteLine("Scale: " + scale);
g.PageUnit = GraphicsUnit.Millimeter;
Font fontMM = new Font("Calibri", 8 * scale);
Console.WriteLine("Font: " + fontMM.Size + " Millimeters: " + g.MeasureString("Test 123", fontMM));
g.DrawString("Test 123", fontMM, new SolidBrush(Color.Black), 10, 10);
}
Returns:
Scale: 1
Font: 8 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.1
Font: 8.8 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.2
Font: 9.6 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.3
Font: 10.4 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.4
Font: 11.2 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.5
Font: 12 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.6
Font: 12.8 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.7
Font: 13.6 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.8
Font: 14.4 Millimeters: {Width=10.99215, Height=3.797873}
Scale: 1.9
Font: 15.2 Millimeters: {Width=10.99215, Height=3.797873}
Conclusion As far as I can tell, the fault here lies with DrawString that doesn't seem to be affected by PageScale. Am I wrong in thinking that this is not the expected behavior?!