C#使用GDI+实现矩形区域内旋转文字

Noah
Noah
发布时间:2026-10-14 | 3人浏览过
原创

在 C# 绘图开发中,常需在受限容器内展示特定角度的文本。由于原生 Graphics.DrawString 仅支持基于固定点的旋转,难以满足矩形区域内的自适应排版需求。本文提供一套完整的解决方案,通过封装 GraphicsText 类,自动处理矩阵变换、包围盒计算及坐标映射,从而精准实现任意角度、任意对齐方式的矩形内旋转文字效果。

核心原理与实现思路

GDI+ 原生绘制旋转文字时,若直接设置旋转变换,文本将围绕原点或指定点旋转,容易超出预设的边界框。为解决此问题,我们需要在绘制前进行两步关键预处理:

  1. 尺寸转换:根据原始字体大小和旋转角度,计算旋转后的最小外接矩形(包围盒)尺寸。
  2. 坐标映射:根据目标矩形区域和对齐方式(如居中、靠上、靠左),反推出合适的旋转中心点。

通过上述两步,我们将“矩形内旋转”转化为标准的“定点旋转”,从而利用成熟的矩阵变换机制完成绘制。

准备工作

本示例基于 .NET Framework 或 .NET Core/5+ 环境,需引入以下命名空间以支持图形操作:

  • System.Drawing:基础绘图结构。
  • System.Drawing.Drawing2D:高级图形功能,包括 Matrix 和 StringFormat。
  • System.Windows.Forms:用于构建测试窗口(可选)。

第一步:实现 GraphicsText 封装类

该类内部包含三个核心方法:ConvertSize 用于计算旋转后的尺寸变化;GetRotatePoint 用于确定对齐所需的旋转中心;主方法 DrawString 负责执行最终的绘制动作。

using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace RotateText
{
    public class GraphicsText
    {
        private Graphics _graphics;

        public Graphics Graphics
        {
            get { return _graphics; }
            set { _graphics = value; }
        }

        /// <summary>
        /// 绘制矩形区域内旋转的文本
        /// </summary>
        public void DrawString(string s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format, float angle)
        {
            // 1. 获取未旋转时的字符串尺寸
            SizeF size = _graphics.MeasureString(s, font);

            // 2. 计算旋转角度下的新包围盒尺寸
            SizeF sizeRotate = ConvertSize(size, angle);

            // 3. 根据布局矩形和对齐方式,计算实际旋转中心点
            PointF rotatePt = GetRotatePoint(sizeRotate, layoutRectangle, format);

            // 4. 重置格式为居中,以便后续定点绘制
            StringFormat newFormat = new StringFormat(format);
            newFormat.Alignment = StringAlignment.Center;
            newFormat.LineAlignment = StringAlignment.Center;

            // 5. 执行基于点的绘制
            DrawString(s, font, brush, rotatePt, newFormat, angle);
        }

        /// <summary>
        /// 基于特定点绘制旋转文本
        /// </summary>
        public void DrawString(string s, Font font, Brush brush, PointF point, StringFormat format, float angle)
        {
            // 保存当前状态以防影响其他绘图操作
            Matrix mtxSave = _graphics.Transform;

            try
            {
                // 创建旋转变换矩阵并应用到 Graphics 对象
                Matrix mtxRotate = new Matrix();
                mtxRotate.RotateAt(angle, point);
                _graphics.Transform = mtxRotate;

                // 在变换后的坐标系中绘制,此时point即为视觉中心
                _graphics.DrawString(s, font, brush, point, format);
            }
            finally
            {
                // 恢复原始变换矩阵
                _graphics.Transform = mtxSave;
            }
        }

        /// <summary>
        /// 计算文本旋转后的外接矩形大小
        /// </summary>
        private SizeF ConvertSize(SizeF size, float angle)
        {
            Matrix matrix = new Matrix();
            matrix.Rotate(angle);

            // 定义原始矩形的四个顶点(相对于中心)
            PointF[] pts = new PointF[4];
            pts[0].X = -size.Width / 2f; pts[0].Y = -size.Height / 2f;
            pts[1].X = -size.Width / 2f; pts[1].Y = size.Height / 2f;
            pts[2].X = size.Width / 2f;  pts[2].Y = size.Height / 2f;
            pts[3].X = size.Width / 2f;  pts[3].Y = -size.Height / 2f;

            // 应用旋转变换
            matrix.TransformPoints(pts);

            // 计算变换后所有顶点的边界范围
            float left = float.MaxValue;
            float right = float.MinValue;
            float top = float.MaxValue;
            float bottom = float.MinValue;

            foreach (PointF pt in pts)
            {
                if (pt.X < left) left = pt.X;
                if (pt.X > right) right = pt.X;
                if (pt.Y < top) top = pt.Y;
                if (pt.Y > bottom) bottom = pt.Y;
            }

            return new SizeF(right - left, bottom - top);
        }

        /// <summary>
        /// 根据目标矩形和对齐方式计算旋转中心
        /// </summary>
        private PointF GetRotatePoint(SizeF size, RectangleF layoutRectangle, StringFormat format)
        {
            PointF pt = new PointF();

            // 处理水平对齐
            switch (format.Alignment)
            {
                case StringAlignment.Near: // 左对齐
                    pt.X = layoutRectangle.Left + size.Width / 2f;
                    break;
                case StringAlignment.Center: // 居中
                    pt.X = (layoutRectangle.Left + layoutRectangle.Right) / 2f;
                    break;
                case StringAlignment.Far: // 右对齐
                    pt.X = layoutRectangle.Right - size.Width / 2f;
                    break;
            }

            // 处理垂直对齐
            switch (format.LineAlignment)
            {
                case StringAlignment.Near: // 顶端对齐
                    pt.Y = layoutRectangle.Top + size.Height / 2f;
                    break;
                case StringAlignment.Center: // 垂直居中
                    pt.Y = (layoutRectangle.Top + layoutRectangle.Bottom) / 2f;
                    break;
                case StringAlignment.Far: // 底端对齐
                    pt.Y = layoutRectangle.Bottom - size.Height / 2f;
                    break;
            }

            return pt;
        }
    }
}

第二步:集成到 Windows Form 测试用例

为了验证效果,我们在 Form 的 OnPaint 事件中调用上述类。示例涵盖了两种典型场景:一是简单的绕点旋转,二是严格限制在矩形区域内的旋转排版。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace RotateText
{
    public partial class FormMain : Form
    {
        private Font _font = new Font("Arial", 12);
        private Brush _brush = new SolidBrush(Color.Black);
        private Pen _pen = new Pen(Color.Black, 1f);
        private string _text = "Crow Soft";

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            GraphicsText graphicsText = new GraphicsText();
            graphicsText.Graphics = e.Graphics;

            // 场景1:基于特定点的自由旋转
            StringFormat format = new StringFormat();
            format.Alignment = StringAlignment.Center;
            format.LineAlignment = StringAlignment.Center;

            graphicsText.DrawString(_text, _font, _brush, new PointF(100, 80), format, 45f);
            graphicsText.DrawString(_text, _font, _brush, new PointF(200, 80), format, -45f);

            // 场景2:矩形区域内的旋转排版
            // 第一行示例
            RectangleF rect = RectangleF.FromLTRB(50, 150, 200, 230);
            
            // 左上对齐 + 30度旋转
            format.Alignment = StringAlignment.Near;
            format.LineAlignment = StringAlignment.Near;
            e.Graphics.DrawRectangle(_pen, rect.Left, rect.Top, rect.Width, rect.Height);
            graphicsText.DrawString(_text, _font, _brush, rect, format, 30f);

            // 右上对齐 + 负30度旋转
            rect.Offset(180, 0);
            format.Alignment = StringAlignment.Far;
            e.Graphics.DrawRectangle(_pen, rect.Left, rect.Top, rect.Width, rect.Height);
            graphicsText.DrawString(_text, _font, _brush, rect, format, -30f);

            // ... 此处省略部分重复代码,实际使用中可遍历配置生成更多案例
        }
    }
}

运行上述代码,您将看到文本严格按照设定的矩形边界和角度分布。具体渲染效果如下所示:

完成判断与注意事项

  • 资源释放:示例中的 Pen、Brush 和 Font 属于非托管资源,在实际生产环境中应确保调用 Dispose() 方法或在 using 语句块中使用,以防止内存泄漏。
  • 性能优化:MeasureString 操作相对耗时,建议在批量绘制相同字体大小的文本时,复用已计算的尺寸结果。
  • 精度问题:浮点数运算可能导致像素级偏差,对于对边缘对齐要求极高的 UI 组件,建议开启 e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality。
以上就是C#使用GDI+实现矩形区域内旋转文字的详细内容,更多关于C#绘图技巧的资料请关注本站其它相关文章!
相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表