.NET下Graphics之文字图片处理
发布日期:2022-03-11 15:03:37 浏览次数:7 分类:技术文章

本文共 12295 字,大约阅读时间需要 40 分钟。

最近因为需要用到图片制作,所以重拾了一下Graphics的相关操作。我们的目的是输入一串字符串,使用特殊字体,生成一张奇数位文字左倾斜15度,偶数位文字右倾斜15度的图片。

1.使用自定义字体(因为字体现在有版权问题,在使用过程中,先确定是否已取得版权)。将自定义字体加入到字体序列集合(PrivateFontCollection)中,并返回其FontFamily,注意,字体路径一定要绝对路径。如下代码:

1 ///  2         /// 添加字体文件到客户字符集合中,并返回当前FontFamily 3         ///  4         /// 字体文件绝对路径 5  6         /// 
7 public FontFamily AddFontToFamily(string fontPath) 8 { 9 if (string.IsNullOrWhiteSpace(fontPath) || !System.IO.File.Exists(fontPath))10 {11 return new FontFamily("黑体");//没有传字体文件,或字体文件不存在,则直接返回系统默认的黑体。12 }13 PrivateFontCollection pfc=new PrivateFontCollection();14 pfc.AddFontFile(fontPath);15 var idxCurrentFont = pfc.Families.Length - 1;16 return pfc.Families[idxCurrentFont];17 }

2.文字处理:

2.1.文字编辑,新建Font对象: 

1 ///  2         /// 相对路径转绝对路径 3         ///  4         ///  5         /// 
6 public string RelativeToAbsPath(string path) 7 { 8 return AppDomain.CurrentDomain.BaseDirectory + path.Replace("/", "\\"); 9 }10 11 /// 12 /// 使用自定义字符集13 /// 14 /// 使用字符集15 /// 字符大小16 /// 字符样式17 ///
字符及样式
18 public Font UseCustomFont(FontFamily fontFamily, int fontSize, FontStyle fontStyle = FontStyle.Regular)19 {20 var font = new Font(fontFamily, fontSize, FontStyle.Regular);21 return font;22 }

2.2.绘制文字,因为每个字可能定义的样式不同,所以这里采用愚笨的方法,单字处理:

1 ///  2         /// 生成正规的单个文字图片(不做任何处理) 3         ///  4         /// 字体 5         /// 内容 6         /// 字体大小 7         /// 字符颜色 8         /// 倾斜角度 9         /// 
10 public Bitmap CreateOneTxtImg(Font font, string content, int fontSize,Color fontColor,int deg=0)11 {12 13 int mapWidth = fontSize + 200;14 15 int mapHeight = fontSize + 200;16 17 Bitmap bitmap = new Bitmap(mapWidth, mapHeight);18 19 Graphics graphics = Graphics.FromImage(bitmap);20 21 graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;//消除锯齿,枚举值22 23 graphics.Clear(Color.Transparent);//设置图片背景色为透明,枚举值,或自定义24 25 var brush=new SolidBrush(fontColor);//画笔并设置颜色26 27 graphics.DrawString(content, font, brush, 0, 0);28 29 //获取字符宽高30 SizeF sizeF = graphics.MeasureString(content, font);31 32 33 #if DEBUG 34 //查证是否和实际字符大小有差距35 graphics.DrawRectangle(new Pen(Color.Chartreuse), new Rectangle(0, 0, (int)sizeF.Width, (int)sizeF.Height));36 //保存图片看效果37 string uploadFileDirectory = "/tempuploads/usernames";38 string fileName = uploadFileDirectory + "/" + TimeStampServices.CurrentTimeSeconds().ToString() + UtilServices.CreateNonceStr(3) + ".png";39 string filePath = AppDomain.CurrentDomain.BaseDirectory + fileName.Replace("/", "\\");40 if (System.IO.File.Exists(filePath))41 {42 System.IO.File.Create(filePath).Close();43 }44 bitmap.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);45 #endif46 47 return bitmap;48 }

上述代码中为什么要加200的大小,是因为文字虽然设定了文字大小,但是实际大小并不是n*n的正方形,并且最终生成的字大小我们没法确定。Graphics.MeasureString测出来的也会有偏差我们看看实际效果,如下图,我们发现使用文字绘制四周会有留空,而且中英文的差异也是显而易见,暂时没找到精确计算字符宽高处理。现阶段可以想到的方案是截掉空白位置,但是需要测量,不能保证所有文字空白位置都是一定的值。这里不做此处理。

既然Graphics.MeasureString获取到的大小必然会大于文字的实际绘制大小,那么我们就用它的大小作为最终的显示大小进行处理(旋转)。

2.2.1.把文字范围外的内容裁掉,因为我们是从(0,0)位置写的文字,所以我们可以使用Graphics.DrawImage(img,0,0)进行处理:

1 ///  2         /// 图片裁剪及旋转操作 3         ///  4         /// 内容大小 5         /// 内容 6         /// 
7 public Bitmap ClipImg(SizeF textSize, Bitmap tempBitmap) 8 { 9 Bitmap bitmap = new Bitmap((int)textSize.Width, (int)textSize.Height);10 Graphics graphics = Graphics.FromImage(bitmap);11 graphics.Clear(Color.Transparent);12 graphics.DrawImage(tempBitmap, 0, 0);//13 graphics.Dispose();14 return bitmap;15 }

2.2.2.裁剪后进行旋转,获取旋转后的宽高,这里需要用到三角函数处理(sin(a+b),sin(a-b))的内容,大家可以补一下,不再赘述整个推导过程。需要注意的是,sin及cos用的度数值都是弧度制,所以需要先把度数转成弧度。代码如下:

1 ///  2         /// 获取旋转后的宽高 3         ///  4         /// 原始宽 5         /// 原始高 6         /// 旋转的角度 7  8         public static SizeF GetRotateSize(int width, int height, int deg) 9         {10             double radian = (deg * Math.PI / 180); ;11             double cos = Math.Cos(radian);12             double sin = Math.Sin(radian);13             float newWidth = (float)(Math.Abs(width * cos) + Math.Abs(height * sin));14             float newHeight = (float)(Math.Abs(width * sin) + Math.Abs(height * cos));15             return new SizeF(newWidth, newHeight);16         }

2.2.3.图片旋转。因为我们是Graphics坐标系中,坐标原点(0,0)是在左上方,所以我们做的处理时:1.把坐标原点移至中心点;2.画布旋转n度;3.把坐标原点移回原坐标原点。有两套方案进行这个操作:

1.矩阵旋转:

1  Matrix matrix = graphics.Transform;2  matrix.RotateAt(deg, new PointF(旋转点x, 旋转点y);3  graphics.Transform = matrix;

 

2.设置graphics:

1 int moveX=偏移量x;2 int moveY=偏移量y;3 graphics.TranslateTransform(moveX, moveY);4 graphics.RotateTransform(deg);5 graphics.TranslateTransform(-moveX, -moveY);

因为我们的画布大小是图片经过旋转后外接矩形的大小,所以此时绘图的初始坐标应该是((int)(旋转后画布大小宽/ 2 - 内容宽 / 2), (int)(旋转后画布大小高/ 2 - 图片高 / 2)),关于这个点,大家也自行脑补一下,不再赘述。采用2.2.3 方法一进行旋转代码如下:

1 ///  2         /// 图片旋转操作 3         ///  4         /// 内容大小 5         /// 内容 6         /// 旋转角度 7         /// 
8 public Bitmap RotateImg(SizeF textSize,Bitmap tempBitmap,int deg) 9 {10 var sizeF = GetRotateSize(textSize.Width, textSize.Height, deg);11 12 Bitmap bitmap = new Bitmap((int)sizeF.Width, (int)sizeF.Height);13 14 Graphics graphics = Graphics.FromImage(bitmap);15 16 graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;//消除锯齿,枚举值17 18 graphics.Clear(Color.Transparent);//设置图片背景色为透明,枚举值,或自定义19 20 21 Matrix matrix = graphics.Transform;22 matrix.RotateAt(deg, new PointF((float)bitmap.Width / 2, (float)bitmap.Height / 2));23 graphics.Transform = matrix;24 25 graphics.DrawImage(tempBitmap, new Rectangle((int)(bitmap.Width / 2 - tempBitmap.Width / 2), (int)(bitmap.Height / 2 - tempBitmap.Height / 2), tempBitmap.Width, tempBitmap.Height));26 27 graphics.ResetTransform();28 29 graphics.Dispose();30 31 return bitmap;32 33 34 }

此时,我们修改一下2.2的CreateOneTxtImg方法:

1 ///  2         /// 生成正规的单个文字图片(不做任何处理) 3         ///  4         /// 字体 5         /// 内容 6         /// 字体大小 7         /// 字符颜色 8         /// 倾斜角度 9         /// 
10 public Bitmap CreateOneTxtImg(Font font, string content, int fontSize,Color fontColor,int deg=0)11 {12 13 int mapWidth = fontSize + 200;14 15 int mapHeight = fontSize + 200;16 17 Bitmap bitmap = new Bitmap(mapWidth, mapHeight);18 19 Graphics graphics = Graphics.FromImage(bitmap);20 21 graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;//消除锯齿,枚举值22 23 graphics.Clear(Color.Transparent);//设置图片背景色为透明,枚举值,或自定义24 25 var brush=new SolidBrush(fontColor);//画笔并设置颜色26 27 graphics.DrawString(content, font, brush, 0, 0);28 29 //获取字符宽高30 SizeF sizeF = graphics.MeasureString(content, font);31 32 var clipBitmap = ClipImg(sizeF, bitmap);33 bitmap.Dispose();34 graphics.Dispose();35 var rotateBitmap = RotateImg(sizeF, clipBitmap, deg);36 clipBitmap.Dispose();37 #if DEBUG 38 //查证是否和实际字符大小有差距39 //graphics.DrawRectangle(new Pen(Color.Chartreuse), new Rectangle(0, 0, (int)sizeF.Width, (int)sizeF.Height));40 //保存图片看效果41 string uploadFileDirectory = "/tempuploads/usernames";42 string fileName = uploadFileDirectory + "/" + TimeStampServices.CurrentTimeSeconds().ToString() + UtilServices.CreateNonceStr(3) + ".png";43 string filePath = AppDomain.CurrentDomain.BaseDirectory + fileName.Replace("/", "\\");44 if (System.IO.File.Exists(filePath))45 {46 System.IO.File.Create(filePath).Close();47 }48 rotateBitmap.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);49 #endif50 51 return bitmap;52 }

可以看到,如下效果:

基本操作已经完成,最后我们计算每个字的宽总和,以及字图的高度最大值,生成最终的图片。

1 ///  2         /// 内容最终宽高 3         ///  4         /// 所有内容 5         /// 
6 7 public Size GetAllWidthAndHeihgt(List
bitmaps) 8 { 9 int width = 0;10 int height = 0;11 foreach (var item in bitmaps)12 {13 width += item.Width;14 if (item.Height > height)15 {16 height = item.Height;17 }18 }19 return new Size(width, height);20 }

内容整合:

1 ///  2         /// 文转图 3         ///  4         /// 字体相对路径 5         /// 字体大小 6         /// 字符颜色 7         /// 需处理的内容 8         /// 生成的图片路径 9         /// 
10 public bool TextToImg(string fontPath, int fontSize, Color fontColor, string textContent = "", string filePath = "")11 {12 if (string.IsNullOrWhiteSpace(fontPath) || string.IsNullOrWhiteSpace(textContent))13 {14 return false;15 }16 17 fontPath = RelativeToAbsPath(fontPath);18 19 if (fontColor.IsEmpty)20 {21 fontColor = Color.FromArgb(255, 255, 255, 255);22 }23 24 var fontFamily = AddFontToFamily(fontPath);25 26 var font = UseCustomFont(fontFamily, fontSize);27 28 List
lstTextImg = new List
();29 30 foreach (var txt in textContent)31 {32 var idxInContent = textContent.IndexOf(txt);//当前字符在字符串的索引位置33 int deg = -15;34 if (idxInContent % 2 == 1)35 {36 deg = 15;37 }38 var bitMap = CreateOneTxtImg(font, txt.ToString(), fontSize, fontColor, deg);39 lstTextImg.Add(bitMap);40 }41 Bitmap endBitmap = CreateResult(lstTextImg);42 endBitmap.Save(filePath, ImageFormat.Png); 43 endBitmap.Dispose(); 44 return true;45 }

 

调用示例,记得生成文件时要释放文件资源,否则会一直被占用中:

1 ///  2         /// 文转图接口测试 3         ///  4         /// 字符颜色 5         /// 字符大小 6         /// 字符串内容 7         /// 
8 public ActionResult TextToImgTest(string fontColor, int fontSize = 60, string textContent = "测试123avenAVEN") 9 {10 Color color = Color.Red;11 if (!string.IsNullOrWhiteSpace(fontColor))12 {13 string[] argb = fontColor.Split(new char[] {
','});14 if (argb.Length == 4)15 {16 color = Color.FromArgb(int.Parse(argb[0]), int.Parse(argb[1]), int.Parse(argb[2]),17 int.Parse(argb[3]));18 }19 }20 //最终保存图片的地址21 string uploadFileDirectory = "/tempuploads/usernames";22 if (Directory.Exists(uploadFileDirectory))23 {24 Directory.CreateDirectory(uploadFileDirectory);25 }26 string fileName = uploadFileDirectory + "/" + TimeStampServices.CurrentTimeSeconds().ToString() + UtilServices.CreateNonceStr(3) + ".png";27 string filePath = AppDomain.CurrentDomain.BaseDirectory + fileName.Replace("/", "\\");28 29 if (!System.IO.File.Exists(filePath))30 {31 System.IO.File.Create(filePath).Close();//创建完后记得释放,否则资源被占用32 }33 34 bool result = TextToImg("/fonts/mfxingyan-noncommercial-regular.ttf", fontSize, color, textContent, filePath);35 return Content(result.ToString());36 }

End

 

本文应该有很多不细腻的地方,比如文字处理方面,获取大小的精度问题,请大家多多指教,如有不对的地方,请指出。有更好的方案处理,也请不吝赐教,感恩!

转载于:https://www.cnblogs.com/aven90/p/9274539.html

转载地址:https://blog.csdn.net/weixin_30267785/article/details/99183533 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:《掌握需求过程》——阅读笔记04
下一篇:div的onclick事件怎么失效了?

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年03月25日 04时24分01秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

《周期》书中的精髓:如何利用周期,掌握世界的发展趋势,实现财富积累。 2019-04-26
《伟大的博弈》书中的精髓:华尔街是如何从一条小街,一步步发展为世界金融中心的。 2019-04-26
《逃不开的经济周期》书中的精髓:经济周期是推动创新变革和经济增长以及复兴的关键力量。 2019-04-26
《朋友还是对手》书中的精髓:奥地利学派和芝加哥学派两派共识远多于分歧,两派首先是朋友,其次才是对手。 2019-04-26
《动物精神》书中的精髓:人类的非理性面影响经济决策,这些有可能是金融危机的根源。 2019-04-26
《赢家的诅咒》书中的精髓:人性的复杂让主流经济学出现了诸多失灵,如何用更多理论完善经济学大厦是经济学家的重要使命 2019-04-26
《巴菲特法则》书中的精髓:用好巴菲特企业前景投资法则,股票投资稳赚不赔。 2019-04-26
《战胜华尔街》书中的精髓:业余投资者如何根据行业特点选好股票,赚得比专业的投资者还要多? 2019-04-26
《巴菲特的估值逻辑》书中的精髓:在投资过程中不断总结经验,不断提升投资能力,不断探索、加深对好公司的理解,成就了巴菲特的投资神话。 2019-04-26
《股市稳赚》书中的精髓:用简单的神奇公式进行股票投资,获得稳定而持久的收益。 2019-04-26
《曾国藩的经济课》书中的精髓:我们如何像曾国藩一样,在遇到道德和环境冲突时,既能保持自己的道德,又能调动资源,帮助自己成事。 2019-04-26
《富人的逻辑》书中的精髓:为什么暴富起来的人会在短期内失去财富,我们又该如何去创造财富和持续拥有财富。 2019-04-26
作文提升~老师整理的优美比喻句太实用 2019-04-26
作文提升~老师整理的优美拟人句太实用 2019-04-26
作文提升~老师整理的优美排比句太实用 2019-04-26
作文提升~老师整理的夸张句,太实用了,赶快收藏 2019-04-26
作文提升~写景常用的拟人句,很经典 2019-04-26
小学教师资格考试科目一思维导图-教育观、教师观和学生观 2019-04-26
小学教师资格考试科目一思维导图-职业道德 2019-04-26
小学教师资格考试科目一思维导图-逻辑思维能力、阅读理解、写作能力 2019-04-26