• C#自定义TemplateImage使用模板底图,运行时根据用户或产品信息生成海报图(1)


    由于经常需要基于固定的一个模板底图,生成微信小程序分享用的海报图,如果每次都调用绘图函数,手动编写每个placeholder的填充,重复而且容易出错,因此,封装一个TemplateImage,用于填充每个需要画上数据的地方,

    先看看调用的方式:

    _homeShareTemplate.Generate(new TemplateItem[]   //Generate返回新的Bitmap
                {
                    new StringTemplateItem() //日期
                    {
                        Location = new Point(80 * 2, 78*2),
                        Font = new Font("宋体", 42, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x8e, 0x1a, 0x22),
                        Value = DateTime.Now.ToString("yyyy.MM.dd"),
                        Horizontal = HorizontalPosition.Center
                    },
                    new StringTemplateItem() //农历
                    {
                        Location = new Point(230*2, 166*2),
                        //MaxWidth = 15,
                        Font = new Font("宋体", 22, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x8e, 0x1a, 0x22),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        Value = GetMonthCalendar(DateTime.Now)
                    },
                    new StringTemplateItem() //星期
                    {
                        Location = new Point(256*2, 175*2),
                        //MaxWidth = 15,
                        Font = new Font("宋体", 24, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x8e, 0x1a, 0x22),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        Value = GetWeekName(DateTime.Now)
                    },
                    new ImageTemplateItem() //图片
                    {
                        Image = (Bitmap) Bitmap.FromFile(Path.Join(Directory.GetCurrentDirectory(),weather.MainImageUrl)),
                        Location = new Point(81*2, 108*2),
                        Size = new Size(132*2, 133*2)
                    },
                    new StringTemplateItem()
                    {
                        Location = new Point(88*2, 257*2),
                        MaxWidth = 125*2,
                        Font = new Font("楷体", 30, FontStyle.Bold, GraphicsUnit.Pixel),
                        Color = Color.FromArgb(0x17, 0x14, 0x0e),
                        Value = weather.Content.Left(44)
                    },
                    new StringTemplateItem() //
                    {
                        Location = new Point(35*2+3,294*2),
                        Color = Color.FromArgb(0x8f, 0x1A, 0x22),
                        Font = new Font("宋体", 38, FontStyle.Bold, GraphicsUnit.Pixel),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        //MaxWidth = 14,
                        Value = weather.Yi.Left(4)
                    },
                    new StringTemplateItem() //
                    {
                        Location = new Point(228*2+3,294*2),
                        Color = Color.FromArgb(0x8f, 0x1A, 0x22),
                        Font = new Font("宋体", 38, FontStyle.Bold, GraphicsUnit.Pixel),
                        StringFormat = new StringFormat(StringFormatFlags.DirectionVertical),
                        //MaxWidth = 14,
                        Value = weather.Ji.Left(4)
                    },
                    new QrCodeTemplateItem() //二维码
                    {
                        Location = new Point(188*2, 421*2),
                        Size = new Size(73*2, 72*2),
                        QrCode = "http://ssssss.com/sdfsdfsdfs/sss"
                    }
                });

    输出的效果如下:

    完整的功能由一个TemplateImage作为模板图管理的类+N个根据需要输出的各种数据处理类,可根据实际需求进行扩展不同的类型,默认有:String,Image,QrCode三种:

    单个模板图管理类的定义:

    public class TemplateImage:IDisposable
    {
            private Bitmap _templateSource = null;
            private Stream _sourceStream = null;
            private FileSystemWatcher _wather = null;
    
            public TemplateImage(Bitmap templateSource)
            {
                _templateSource = templateSource;
            }
    
            /// <summary>
            /// 模板图片的构造函数
            /// </summary>
            /// <param name="templatePath">模板图片文件绝对路径</param>
            /// <param name="isWatchFileModify">是否自动监控文件,当文件有变动时,自动重新加载模板文件
            /// </param>
            public TemplateImage(string templatePath,bool isWatchFileModify=true)
            {
                if (!File.Exists(templatePath))
                {
                    throw new FileNotFoundException(nameof(templatePath));
                }
    
                //打开模板文件路径,在跳出构造函数后,自动释放file对象,防止长久占用文件,导致无法替换模板文件
                using var file = File.OpenRead(templatePath);
    
                var data = file.ReadAllBytes(); 
    
                var s1 = new ByteStream(data);  //这里s1肯定不能关闭,否则,再调用Bitmap.Clone函数的时候,会报错
                _sourceStream = s1;
                _templateSource = (Bitmap) Bitmap.FromStream(s1);
    
                if (isWatchFileModify) //如果启用文件监控,则自动监控模板图片文件
                {
                    _wather = new FileSystemWatcher(templatePath);
                    _wather.EnableRaisingEvents = true;
                    _wather.Changed += wather_changed;
    
                }
            }
    
            private void wather_changed(object sender, FileSystemEventArgs e)
            {
                if (e.ChangeType == WatcherChangeTypes.Changed || e.ChangeType== WatcherChangeTypes.Created )
                {
                    using var file = File.OpenRead(e.FullPath);
    
                    var data = file.ReadAllBytes();
    
                    var oldValue = _sourceStream;
                    var templateSource = _templateSource;
                    var s1 = new ByteStream(data);
                    var newTemplateSource = (Bitmap) Bitmap.FromStream(s1);
                    
                    _sourceStream = s1;
                    _templateSource = newTemplateSource;
                    
                    oldValue.Close();
                    oldValue.Dispose();
                    templateSource.Dispose();
                }
            }
            
    
            public SmoothingMode SmoothingMode { set; get; } = SmoothingMode.AntiAlias;
    
            public TextRenderingHint TextRenderingHint { set; get; } = TextRenderingHint.AntiAlias;
    
            public CompositingQuality CompositingQuality { set; get; } = CompositingQuality.HighQuality;
    
            /// <summary>
            /// 根据传入的数据,套入模板图片,生成新的图片
            /// </summary>
            /// <param name="settings"></param>
            /// <returns></returns>
            public Bitmap Generate(TemplateItemBase[] settings)
            {
                //Clone一个新的Bitmap对象
                var newImg = (Bitmap)_templateSource.Clone();
                
                var g1 = Graphics.FromImage(_templateSource);
                
                try
                {
                    using (var g = Graphics.FromImage(newImg))
                    {
                        g.SmoothingMode = SmoothingMode;
                        g.TextRenderingHint = TextRenderingHint;
                        g.CompositingQuality = CompositingQuality;
                
                        foreach (var item in settings)
                        {
                            item.Draw(g, newImg.Size); //调用每个Item的Draw画入新的数据
                        }
    
                        return newImg;
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
                
            }
                    
            public void Dispose()
            {
                _templateSource.Dispose();
                
                _sourceStream?.Close();
                _sourceStream?.Dispose();
            }
        }    

    至此,一个模板图片类已定义完成,接下来需要定义一个Placeholder的基类:

     1     public abstract class TemplateItemBase
     2     {
     3         /// <summary>
     4         /// 水平方向对其方式,默认为Custom,使用Location定位
     5         /// </summary>
     6         public HorizontalPosition Horizontal { set; get; } = HorizontalPosition.Custom;
     7 
     8         /// <summary>
     9         /// 垂直方向对其方式,默认为Custom,使用Location定位
    10         /// </summary>
    11         public VerticalPosition Vertical { set; get; } = VerticalPosition.Custom;
    12      
    13         /// <summary>
    14         /// 输出项定位
    15         /// </summary>
    16         public Point Location { set; get; }
    17 
    18         public abstract void Draw(Graphics graphics,Size newBitmapSize);
    19 
    20     }

    这个基类定义了每个placeholder的定位方式,Custom表示使用Location自定义位置.

    然后开始来定义每个不同类型的TemplateItem:

    1.String类型:

     1     /// <summary>
     2     /// 普通字符串项
     3     /// </summary>
     4     public class StringTemplateItem : TemplateItemBase
     5     {
     6         /// <summary>
     7         /// 文本字符串值
     8         /// </summary>
     9         public string Value { set; get; }
    10         
    11         /// <summary>
    12         /// 字体信息
    13         /// </summary>
    14         public Font Font { set; get; }
    15         
    16         /// <summary>
    17         /// 字体颜色
    18         /// </summary>
    19         public Color Color { set; get; }= Color.Black;
    20 
    21         /// <summary>
    22         /// 文本输出的最大宽度,如果为0,则自动,,如果非0,则只用最大宽度,并自动根据最大宽度修改计算字符串所需高度
    23         /// </summary>
    24         public int MaxWidth { set; get; } = 0;
    25 
    26         /// <summary>
    27         /// 字符串输出参数
    28         /// </summary>
    29         /// <example>
    30         /// 如纵向输出:
    31         /// new StringFormat(StringFormatFlags.DirectionVertical)
    32         /// 
    33         /// </example>
    34         public StringFormat StringFormat { set; get; }
    35 
    36         public override void Draw(Graphics graphics,Size newBitmapSize)
    37         {
    38             var location = this.Location;
    39             SizeF size=default(Size);
    40             if (this.Horizontal== HorizontalPosition.Center || this.Vertical== VerticalPosition.Middle)
    41             {
    42                 location = new Point(this.Location.X,this.Location.Y);
    43                 
    44                 if (this.MaxWidth>0)
    45                 {
    46                     size = graphics.MeasureString(this.Value, this.Font,this.MaxWidth);
    47                 }
    48                 else
    49                 {
    50                     size = graphics.MeasureString(this.Value, this.Font);
    51                 }
    52                         
    53                 if (this.Horizontal== HorizontalPosition.Center)
    54                 {
    55                     var newx = newBitmapSize.Width / 2 - (int)(size.Width / 2);
    56                     location.X = newx;
    57                 }
    58 
    59                 if (this.Vertical== VerticalPosition.Middle)
    60                 {
    61                     var newy= newBitmapSize.Height / 2 - (int)(size.Height / 2);
    62                     location.Y = newy;
    63                 }
    64             }
    65             else if(MaxWidth>0)
    66             {
    67                 size = graphics.MeasureString(this.Value, this.Font,this.MaxWidth);
    68             }
    69 
    70             if (MaxWidth>0)
    71             {
    72                 graphics.DrawString(this.Value, this.Font,new SolidBrush(this.Color), new RectangleF(location,size),StringFormat);
    73             }
    74             else
    75             {
    76                 graphics.DrawString(this.Value, this.Font,new SolidBrush(this.Color), location,StringFormat);
    77             }
    78             
    79             
    80         }
    81     }

    2.纯图片类型:

     1     /// <summary>
     2     /// 传入一个图片
     3     /// </summary>
     4     public class ImageTemplateItem:TemplateItemBase
     5     {
     6         /// <summary>
     7         /// 图片数据
     8         /// </summary>
     9         public Bitmap Image { set; get; }
    10         
    11         /// <summary>
    12         /// 图片输出到模板图的时候的大小
    13         /// </summary>
    14         public Size Size { set; get; }
    15         
    16         public override void Draw(Graphics graphics,Size newBitmapSize)
    17         {
    18             var location = this.Location;
    19             
    20             //计算垂直居中或水平居中的情况下的定位
    21             if (this.Horizontal== HorizontalPosition.Center || this.Vertical== VerticalPosition.Middle)
    22             {
    23                 location = new Point(this.Location.X,this.Location.Y);
    24                         
    25                 if (this.Horizontal== HorizontalPosition.Center)
    26                 {
    27                     var newx = newBitmapSize.Width / 2 - this.Size.Width / 2;
    28                     
    29                     location.X = newx;
    30                 }
    31 
    32                 if (this.Vertical== VerticalPosition.Middle)
    33                 {
    34                     var newy= newBitmapSize.Height / 2 - this.Size.Height / 2;
    35                     location.Y = newy;
    36                 }
    37             }
    38             
    39             //此处后续可优化为使用Lockbits的方式
    40             graphics.DrawImage(Image,new Rectangle(location,this.Size),new Rectangle(0,0,this.Image.Width,this.Image.Height),GraphicsUnit.Pixel);
    41             
    42         }
    43     }

     3.QrCode的方式,使用QRCoder类库:

     1     /// <summary>
     2     /// 二维码项
     3     /// </summary>
     4     public class QrCodeTemplateItem : TemplateItemBase
     5     {
     6         /// <summary>
     7         /// 二维码内实际存储的字符数据
     8         /// </summary>
     9         public string QrCode { set; get; }
    10         
    11         /// <summary>
    12         /// 二维码中心的icon图标
    13         /// </summary>
    14         public Bitmap Icon { set; get; }
    15         
    16         /// <summary>
    17         /// 二维码尺寸
    18         /// </summary>
    19         public Size Size { set; get; }
    20 
    21         /// <summary>
    22         /// 容错级别,默认为M
    23         /// </summary>
    24         public QRCodeGenerator.ECCLevel ECCLevel { set; get; } = QRCodeGenerator.ECCLevel.M;
    25         
    26         public override void Draw(Graphics graphics,Size newBitmapSize)
    27         {
    28             var location = this.Location;
    29             
    30             if (this.Horizontal== HorizontalPosition.Center || this.Vertical== VerticalPosition.Middle)
    31             {
    32                 location = new Point(this.Location.X,this.Location.Y);
    33                         
    34                 if (this.Horizontal== HorizontalPosition.Center)
    35                 {
    36                     var newx = newBitmapSize.Width / 2 - this.Size.Width / 2;
    37                     
    38                     location.X = newx;
    39                 }
    40 
    41                 if (this.Vertical== VerticalPosition.Middle)
    42                 {
    43                     var newy= newBitmapSize.Height / 2 - this.Size.Height / 2;
    44                     location.Y = newy;
    45                 }
    46             }
    47             
    48             using (QRCodeGenerator qrGenerator = new QRCodeGenerator())
    49             using (QRCodeData qrCodeData = qrGenerator.CreateQrCode(QrCode,ECCLevel))
    50             using (QRCode qrCode = new QRCode(qrCodeData))
    51             using (Bitmap qrCodeImage = qrCode.GetGraphic(20,Color.Black,Color.White,Icon))
    52             {
    53                 graphics.DrawImage(qrCodeImage,new Rectangle(location,this.Size),new Rectangle(0,0,qrCodeImage.Width,qrCodeImage.Height),GraphicsUnit.Pixel);
    54                 
    55             }
    56         }
    57     }

    后续的优化:

    1.Image画入的优化处理,考虑是否可以用Lockbits进行优化

    2.增加不同类型的新的Item

    完整的代码详见:https://github.com/kugarliyifan/Kugar.Core/blob/master/Kugar.Core.NetCore/Images/TemplateImage.cs

  • 相关阅读:
    Pig与Hive的区别
    Hadoop MapReduceV2(Yarn) 框架简介
    Spark技术内幕:Client,Master和Worker 通信源码解析
    Spark技术内幕:Stage划分及提交源码分析
    无责任比较thrift vs protocol buffers
    理解hadoop的Map-Reduce数据流(data flow)
    hadoop-2.5安装与配置
    linux下查看本地程序占用的端口
    MFC安装与部署(程序打包)
    关系数据库设计中数据字典设计例子
  • 原文地址:https://www.cnblogs.com/kugar/p/14209584.html
Copyright © 2020-2023  润新知